resolve
blobmap.resolve ¶
Reading the format: object key to blob id.
This lives in blobmap rather than in the consuming service because it is the semantics of the manifest. If a consumer reimplements the parse rules, the format and its interpretation drift, and the failure mode is silent misattribution of chunks to blobs.
The trie holds one node per cut, never per object. A store with 300,000 objects contributes a handful of nodes, and a bucketed array is a single node however many blobs it spans, because the id is arithmetic rather than an entry. A petabyte at 100 GB blobs is single digit megabytes of Python dicts.
Example
from blobmap.model import Blob, Bucket, Manifest manifest = Manifest("cordex/a.zarr", ... (Blob("b_tas", ("tas/c",), ... Bucket(0, 2048, "v3_slash")),), ... ("**/zarr.json",)) trie = Trie() trie.add(manifest) trie.lookup("cordex/a.zarr/tas/c/5000/0/0").blob_id 'b_tas_2' trie.lookup("cordex/a.zarr/zarr.json").kind 'hot' trie.lookup("somewhere/else/entirely").kind 'unmanaged'
Resolution
dataclass
¶
Resolution(
kind: str,
blob_id: str | None = None,
blob: Blob | None = None,
bucket_index: int | None = None,
)
What a key resolves to.
Attributes:
| Name | Type | Description |
|---|---|---|
kind |
str
|
One of |
blob_id |
str | None
|
The concrete id, such as |
blob |
Blob | None
|
The definition that matched, for callers that need its prefixes or bucket. |
bucket_index |
int | None
|
Which bucket the key fell into, 0 for an unbucketed blob. |
Example
Resolution("hot").archivable False
archivable
property
¶
archivable: bool
Whether this key may be moved to tape.
Returns:
| Type | Description |
|---|---|
bool
|
True only for |
bool
|
unmanaged keys all stay hot. |
Trie ¶
Trie()
Segment-wise longest prefix match over all known manifests.
Deepest declaration wins, so a store that outgrows a parent scope manifest and gets its own simply overrides it for that subtree. There is no special case in the lookup and nesting needs no validation.
Build once at startup from
ManifestStore.load_all,
then rebind atomically on reload. In-flight lookups finish against the
old trie and the next one sees the new, so no lock is needed.
Example
from blobmap.model import Blob, Manifest trie = Trie() trie.add_all([Manifest("a", (Blob("b_a", ("x",)),), ()), ... Manifest("a/x", (Blob("b_deep", ("y",)),), ())]) trie.lookup("a/x/y/0").blob_id # deepest declaration wins 'b_deep_0'
Source code in src/blobmap/resolve.py
104 105 106 107 108 109 110 111 112 | |
epochs
property
¶
epochs: dict[str, int]
Epoch of every indexed scope.
Returns:
| Type | Description |
|---|---|
dict[str, int]
|
Mapping of scope to epoch, for deciding whether a reload is |
dict[str, int]
|
needed without diffing the definitions. |
add ¶
add(manifest: Manifest) -> None
Insert one manifest's declarations.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
Manifest
|
The manifest to index. Its scope is prepended to every prefix, so keys are matched absolutely. |
required |
Source code in src/blobmap/resolve.py
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | |
add_all ¶
add_all(manifests: Iterable[Manifest]) -> None
Insert many manifests.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifests
|
Iterable[Manifest]
|
Usually the result of |
required |
Source code in src/blobmap/resolve.py
140 141 142 143 144 145 146 147 | |
lookup ¶
lookup(key: str) -> Resolution
Map an absolute object key to a blob.
Misses are normal and safe. An unpartitioned store, foreign data or a brand new upload resolves to unmanaged, which means hot. No database call and no exception. Only registered blobs are archivable, so a miss is the conservative default rather than something to handle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
key
|
str
|
Full object key, including the scope prefix. |
required |
Returns:
| Type | Description |
|---|---|
Resolution
|
A |
Example
from blobmap.model import Blob, Bucket, Manifest trie = Trie() trie.add(Manifest("s", (Blob("b_tas", ("tas/c",), ... Bucket(0, 100, "v3_slash")),), ())) trie.lookup("s/tas/c/250/0/0").bucket_index 2 trie.lookup("s/tas/c/notanumber/0").kind 'unmanaged'
Source code in src/blobmap/resolve.py
185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 | |
resolve ¶
resolve(manifest: Manifest, key: str) -> Resolution
Resolve one key against one manifest.
Builds a throwaway trie, so this is for tests and one-off inspection. A
service should build a Trie once and reuse it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manifest
|
Manifest
|
The manifest to resolve against. |
required |
key
|
str
|
Key relative to the manifest scope, unlike
|
required |
Returns:
| Type | Description |
|---|---|
Resolution
|
A |
Example
from blobmap.model import Blob, Manifest resolve(Manifest("s", (Blob("b_pr", ("pr/c",)),), ()), ... "pr/c/7/0/0").blob_id 'b_pr_0'
Source code in src/blobmap/resolve.py
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 | |