pub struct MeshBlobAdapter { /* private fields */ }Expand description
mesh://-scheme adapter that stores chunks as content-addressed
RedexFiles. See the
module-level docs for the dispatch shape.
Implementations§
Source§impl MeshBlobAdapter
impl MeshBlobAdapter
Sourcepub fn new(id: impl Into<String>, redex: Arc<Redex>) -> MeshBlobAdapter
pub fn new(id: impl Into<String>, redex: Arc<Redex>) -> MeshBlobAdapter
Construct a mesh-native adapter rooted at redex. Chunks are
stored as in-memory RedexFiles by default — call
Self::with_persistent to write to disk (requires the
underlying Redex to be configured with a persistent dir),
and / or Self::with_replication to opt every per-chunk
file into the cross-node replication runtime.
Sourcepub fn with_auto_repair_on_fetch(self, enabled: bool) -> MeshBlobAdapter
pub fn with_auto_repair_on_fetch(self, enabled: bool) -> MeshBlobAdapter
Enable fetch-path opportunistic auto-repair for RS-encoded
blobs. When set, every successful reconstruction inside
fetch_range re-stores the missing data chunks under
their original content-addressed hashes — so the stripe
goes back to healthy, the v0.3 Phase C6 GC stripe-pin
lifts naturally, and subsequent fetches don’t re-pay the
reconstruction cost.
Default is false — the v0.3 plan’s stated semantic is
that fetch never writes. Enable for hot-blob workloads
where degraded stripes would otherwise re-reconstruct on
every read. The operator-driven Self::repair_blob
remains the durable, sweep-the-whole-blob recovery path
regardless of this flag.
Sourcepub fn with_persistent(self, persistent: bool) -> MeshBlobAdapter
pub fn with_persistent(self, persistent: bool) -> MeshBlobAdapter
Opt every per-chunk file into disk persistence. Default is in-memory; switch on for production deployments that want blob chunks to survive process restart.
Sourcepub fn with_tree_node_cache(self, cap_bytes: usize) -> MeshBlobAdapter
pub fn with_tree_node_cache(self, cap_bytes: usize) -> MeshBlobAdapter
Attach a manifest-tree LRU cache for BlobRef::Tree walks.
cap_bytes sets the byte budget — every walk_tree_range
fetch consults the cache first and stores the fetched node
bytes on miss. A second range read on the same blob whose
path overlaps the prior walk’s path skips the
fetch_chunk for the cached nodes.
Default 64 MiB cap ≈ 13 K nodes at the typical ~5 KiB
postcard-encoded per-node size. Operators with tighter or
looser memory budgets pass an explicit cap_bytes. Pass
0 to disable caching entirely (every lookup misses,
every insert is a no-op — useful for ablation testing).
Cache hits stay correct under the content-addressed model (BLAKE3 hashes are immutable by construction); no invalidation surface is exposed.
Sourcepub fn with_chunk_file_max_memory_bytes(self, bytes: usize) -> MeshBlobAdapter
pub fn with_chunk_file_max_memory_bytes(self, bytes: usize) -> MeshBlobAdapter
Override the per-chunk-file max_memory_bytes reservation.
RedexFileConfig defaults to 64 MiB per channel; for blobs
stored as many small chunks (e.g. 8 KiB chunks of a multi-
MiB blob) that reservation is multiplied by the chunk
count, easily blowing the process commit limit even though
each channel only ever holds a few KiB of live bytes.
Operators with chunk-heavy blobs pass a smaller value here
(e.g. 1 << 20 = 1 MiB) to bound the reservation.
The upstream min(value, 64 MiB) clamp still applies — a
larger value than the default has no effect.
Sourcepub fn tree_node_cache_stats(&self) -> Option<(u64, u64, usize, usize)>
pub fn tree_node_cache_stats(&self) -> Option<(u64, u64, usize, usize)>
Snapshot of the tree-node cache’s (hits, misses, bytes, len) for operator metrics. Returns None when no cache
is wired.
Sourcepub fn with_replication(self, cfg: ReplicationConfig) -> MeshBlobAdapter
pub fn with_replication(self, cfg: ReplicationConfig) -> MeshBlobAdapter
Per-chunk replication config applied to every newly-opened
chunk file. Requires Redex::enable_replication(mesh) to
have been called on the underlying handle; the per-chunk
open surfaces a typed RedexError if not.
Sourcepub fn with_retention_floor(self, floor: Duration) -> MeshBlobAdapter
pub fn with_retention_floor(self, floor: Duration) -> MeshBlobAdapter
Override the default retention floor (24 h) applied by the GC sweep. Shorter floors reclaim disk faster at the cost of premature GC under racy refcount sources; longer floors are safer but consume more disk between sweeps. Tune to match the operator’s chain-fold cadence.
Sourcepub fn with_disk_capacity(self, bytes: u64) -> MeshBlobAdapter
pub fn with_disk_capacity(self, bytes: u64) -> MeshBlobAdapter
Operator-configured disk capacity in bytes. Drives the
dataforts_blob_disk_capacity_bytes gauge + the health-
gate threshold. 0 (the default) disables the health
gate entirely.
Sourcepub fn with_auth_guard(self, guard: Arc<AuthGuard>) -> MeshBlobAdapter
pub fn with_auth_guard(self, guard: Arc<AuthGuard>) -> MeshBlobAdapter
Wire an AuthGuard handle so the *_authorized variants
of Self::pin / Self::unpin / Self::delete_chunk
can gate peer-initiated ops against the publishing chain’s
(origin_hash, ChannelName) ACL. The unauth variants stay
reachable for system-internal callers (GC sweep,
chain-fold-driven refcount maintenance).
Sourcepub fn with_blob_heat(
self,
registry: Arc<Mutex<RawMutex, BlobHeatRegistry>>,
half_life: Duration,
) -> MeshBlobAdapter
pub fn with_blob_heat( self, registry: Arc<Mutex<RawMutex, BlobHeatRegistry>>, half_life: Duration, ) -> MeshBlobAdapter
Wire a shared blob-heat registry. Each successful fetch
then bumps the chunk hash’s heat counter so a gravity
tick can observe the read rate (PR-5j-b). The registry
handle is cheap to clone (Arc<Mutex> inside); operators
typically share the same handle with the gravity migration
controller’s tick loop.
half_life controls the per-counter decay; pass
DEFAULT_BLOB_HEAT_HALF_LIFE for the standard 60 s
half-life or a custom value when tuning aggressive vs
lazy migration cadence.
Sourcepub fn with_overflow(self, config: OverflowConfig) -> MeshBlobAdapter
pub fn with_overflow(self, config: OverflowConfig) -> MeshBlobAdapter
Install the supplied OverflowConfig as the initial
overflow state. The enabled field of config is
honored — passing OverflowConfig { enabled: true, .. Default::default() } is the typical “turn on with
defaults” gesture. Subsequent
Self::set_overflow_enabled / Self::set_overflow_config
calls override the state set here.
Default (no call to this builder) is
OverflowConfig::default() with enabled = false —
the v0.2 pull-only posture.
Sourcepub fn overflow_enabled(&self) -> bool
pub fn overflow_enabled(&self) -> bool
True iff the adapter is currently advertising
dataforts.blob.overflow and accepting inbound
OverflowPush requests. Cheap (one read-lock acquire);
fine to call on the hot path.
Returns the runtime state, so operators dashboarding “is overflow on” against a recently-toggled node see the live value rather than a build-time snapshot.
Sourcepub fn overflow_config(&self) -> OverflowConfig
pub fn overflow_config(&self) -> OverflowConfig
Snapshot of the current overflow configuration. Returns
a copy of the OverflowConfig (it’s Copy); the lock
is released before the return. Inspection-only; mutate
via Self::set_overflow_enabled or
Self::set_overflow_config.
Sourcepub fn set_overflow_enabled(&self, enabled: bool)
pub fn set_overflow_enabled(&self, enabled: bool)
Flip the overflow master switch at runtime. No-op if
enabled matches the current state. When the boolean
transitions, the adapter’s next capability rebroadcast
adds (or removes) the dataforts.blob.overflow tag —
peers see the change on the following announcement
cycle.
The adapter doesn’t hold a MeshNode handle (the two
are intentionally decoupled), so the rebroadcast itself
happens through one of:
MeshNode::announce_blob_overflow_state(adapter)— the convenience path: snapshots local caps, syncs thedataforts.blob.overflowtag to the adapter’s current state, and announces in one call. Recommended.- Manual
announce_capabilities(updated_set)whereupdated_setcarries the matching presence tag.
Until the rebroadcast lands, the sender-side overflow
tick short-circuits (the local caps snapshot doesn’t yet
reflect the new state — see
drive_blob_overflow_tick) and peers reject any inbound
nudge as SenderNotOverflowing.
Cheap: one write-lock acquire, one bool store. Safe to
call concurrently with reads via
Self::overflow_enabled — the RwLock ensures the
observed value is consistent with one toggle event.
Sourcepub fn set_overflow_config(&self, config: OverflowConfig)
pub fn set_overflow_config(&self, config: OverflowConfig)
Replace the entire overflow configuration in one call.
Useful when the operator wants to update thresholds
(high-water, low-water, push budget, scope) without
touching the master switch — pass the same enabled
value the adapter currently has, plus the new
thresholds. Or use this to atomically enable + tune in
one call.
Sourcepub fn overflow_active(&self) -> bool
pub fn overflow_active(&self) -> bool
True iff the most recent overflow tick observed local
disk at or above the high-water threshold (i.e. the
controller is actively shedding). Mirrors the
hysteresis state machine — stays true through the
hysteresis band on the way down and only flips back to
false once disk drops to or below the low-water
threshold.
Read-only observer; the tick driver is the single writer. Cheap (one atomic load) — safe to call on a dashboard hot path.
Sourcepub async fn drive_overflow_tick(
&self,
ctx: OverflowTickContext<'_>,
size_for_hash: impl Fn([u8; 32]) -> Option<u64>,
) -> BlobOverflowTickReport
pub async fn drive_overflow_tick( &self, ctx: OverflowTickContext<'_>, size_for_hash: impl Fn([u8; 32]) -> Option<u64>, ) -> BlobOverflowTickReport
Convenience: drive one overflow tick + auto-record the
resulting report into the adapter’s metrics registry.
Composes super::overflow::drive_blob_overflow_tick
with super::metrics::BlobMetrics::record_overflow_tick
so operators don’t have to thread the report through
two calls on every tick.
ctx carries everything the controller needs that the
adapter doesn’t already own: the capability index, the
heat registry, the sink, the local caps snapshot, and
the disk-usage stats. The adapter contributes the
refcount, config, and overflow_active hysteresis
state from self. The closure size_for_hash stays
separate (closures don’t sit in struct fields without
a Box<dyn Fn> wrapper that’s heavier than the
inlined-impl-Fn shape).
The controller’s config is read live from
self.overflow_config() so an operator-toggled
threshold lands on the next tick.
Returns the super::overflow::BlobOverflowTickReport
so callers can inspect per-tick state without a second
metrics scrape.
Sourcepub fn record_overflow_reject(&self, reason: OverflowReject)
pub fn record_overflow_reject(&self, reason: OverflowReject)
Bump the receive-side overflow rejection counter for
reason. Called by
super::overflow::OverflowPushHandler on every
inbound push that admission rejects; surfaces in the
adapter’s Prometheus body as
dataforts_blob_overflow_rejected_total{reason}.
The sender’s own metrics bump
dataforts_blob_overflow_push_errors_total on the same
event (via the controller’s push_errors counter);
the two surfaces are complementary so operators
dashboarding either side see matching volumes.
Sourcepub fn blob_heat_enabled(&self) -> bool
pub fn blob_heat_enabled(&self) -> bool
True iff this adapter is wired to bump a shared blob-heat registry on fetch.
Sourcepub async fn tick_blob_heat(
&self,
policy: &DataGravityPolicy,
sink: &dyn BlobHeatSink,
) -> Result<u64, BlobError>
pub async fn tick_blob_heat( &self, policy: &DataGravityPolicy, sink: &dyn BlobHeatSink, ) -> Result<u64, BlobError>
Run one tick of the blob-heat registry: walk every tracked
hash, apply decay, ask the supplied policy whether to
emit, and route each Emit { rate } / Withdraw decision
through sink (as announce_blob_heat_batch). Returns
the count of emissions that landed (Emit + Withdraw
combined). PR-5j-c emission path; operators drive from a
periodic task at DataGravityPolicy::emit_interval
cadence.
No-op (Ok(0)) when no registry is wired. The emission
snapshot is taken under the registry lock; the lock is
released before awaiting the sink, so a concurrent
fetch on this adapter can keep bumping heat in parallel.
The lock is !Send across .await — holding it past an
await would also break the runtime’s task model (a task
rescheduled to a different worker while holding a thread-
affine guard) — which is the real concern. parking_lot
mutexes don’t poison; the explicit scoping below is about
preserving Send for the awaited future.
Pin hash against GC, gated by an
AuthGuard::is_authorized_full check on
(origin_hash, channel). Returns
BlobError::Backend if the adapter has no guard
configured (operator misconfiguration on the peer-facing
path) or if the caller is not authorized for channel.
channel is the canonical name of the chain that
originally published the blob — the caller of the pin op
must be authorized on that chain.
Unpin hash, gated by an
AuthGuard::is_authorized_full check on
(origin_hash, channel). Returns
BlobError::Backend if no guard is configured or the
caller is not authorized.
Delete a single chunk file by content hash, gated by an
AuthGuard::is_authorized_full check on
(origin_hash, channel). Mirrors
Self::delete_chunk on the success path; returns a typed
BlobError::Backend if no guard is configured or the
caller is not authorized.
System-internal callers (the GC sweep) use the unauth
Self::delete_chunk variant — only peer-initiated
deletes route through this gate.
Sourcepub fn refcount_table(&self) -> &BlobRefcountTable
pub fn refcount_table(&self) -> &BlobRefcountTable
Refcount table reference. Operators bump via
BlobRefcountTable::incr from chain-fold / CortEX
integration sites; the adapter reads on sweep + stat
paths.
Sourcepub fn metrics(&self) -> &BlobMetrics
pub fn metrics(&self) -> &BlobMetrics
Atomic-counter registry surfaced for Prometheus scrape.
Sourcepub fn prometheus_text(&self) -> String
pub fn prometheus_text(&self) -> String
Render a Prometheus-text snapshot for the operator scrape.
Concatenates the counter / gauge bodies with the live
gc_pending_total from the refcount table.
Sourcepub fn pin(&self, hash: [u8; 32], now_unix_ms: u64)
pub fn pin(&self, hash: [u8; 32], now_unix_ms: u64)
Pin hash against GC. Operator escape hatch — pinned
hashes survive sweep regardless of refcount + retention
floor. Returns the hash for ergonomic chaining.
now_unix_ms should be the operator’s current wall-clock
— used to stamp last_seen and (if the hash is new)
first_seen.
Sourcepub fn unpin(&self, hash: [u8; 32], now_unix_ms: u64)
pub fn unpin(&self, hash: [u8; 32], now_unix_ms: u64)
Unpin hash. After this, the hash returns to the normal
refcount / retention-floor sweep contract.
Sourcepub async fn sweep_gc(
&self,
now_unix_ms: u64,
disk_pressure_critical: bool,
) -> Result<u64, BlobError>
pub async fn sweep_gc( &self, now_unix_ms: u64, disk_pressure_critical: bool, ) -> Result<u64, BlobError>
Run a GC sweep. Pure-logic in two halves: decide (which
hashes are deletable under the refcount + retention +
pressure + pin rules), then act (delete the chunk files,
remove the refcount entries, bump
dataforts_blob_gc_swept_total). The two halves are
fused here for the typical operator-driven sweep; advanced
callers can invoke
BlobRefcountTable::deletable_hashes +
Self::delete_chunk directly for dry-run / batched
flows.
Returns the count of chunks actually swept (may be less
than deletable_hashes if some chunk-file deletes failed —
the failures are logged but the refcount entry is left in
place so the next sweep retries).
Sourcepub async fn delete_chunk(&self, hash: &[u8; 32]) -> Result<(), BlobError>
pub async fn delete_chunk(&self, hash: &[u8; 32]) -> Result<(), BlobError>
Delete a single chunk file by content hash. The chunk’s
RedexFile is closed + removed from the Redex manager
(including any on-disk segment dir for persistent
deployments), and the refcount table entry is dropped on
success so stat() stops surfacing a stale
last_seen_unix_ms for a deleted blob and any subsequent
re-store starts a fresh retention-floor clock. Idempotent
on the success path — closing an already-closed file
returns Ok(()) from the Redex layer. Used by the
peer-initiated Self::delete_chunk_authorized as a
force-delete; reachable directly for operators running
batched / dry-run flows against
BlobRefcountTable::deletable_hashes.
On Err the refcount entry is preserved so the next sweep
can retry — chunk-file close failures shouldn’t strand the
retention clock.
The GC sweep does NOT route through this method: it uses
BlobRefcountTable::remove_if_deletable + a direct
close_and_unlink_file so an incr racing the sweep can
rescue the entry without losing data.
Sourcepub async fn store_stream_tree(
&self,
stream: Pin<Box<dyn Stream<Item = Result<Bytes, BlobError>> + Send>>,
encoding: Encoding,
chunking: ChunkingStrategy,
) -> Result<BlobRef, BlobError>
pub async fn store_stream_tree( &self, stream: Pin<Box<dyn Stream<Item = Result<Bytes, BlobError>> + Send>>, encoding: Encoding, chunking: ChunkingStrategy, ) -> Result<BlobRef, BlobError>
Store a byte stream as a hierarchical-manifest blob
(BlobRef::Tree). Returns the constructed reference;
every constituent chunk + tree node is persisted before
the return.
Streams are consumed chunk-by-chunk against the supplied
ChunkingStrategy. v0.3 Phase A accepts only
ChunkingStrategy::Fixed at exactly
BLOB_CHUNK_SIZE_BYTES (4 MiB) — CDC lands in Phase B,
other fixed sizes break v0.2 chunk-level dedup and are
rejected. Each chunk is hashed (BLAKE3), persisted via
the existing store_chunk path (idempotent on hash
collision), then fed into a TreeBuilder that
accumulates the manifest tree incrementally.
Memory bound: O(chunk_size + TREE_FANOUT × MAX_TREE_DEPTH × entry_size) — roughly 4 MiB + 20 KiB at the v0.3a defaults. Independent of total stream size; a 1 TiB stream uses the same peak memory as a 1 GiB stream.
Phase A ships with sequential store_chunk dispatch —
each chunk is awaited before the next is requested.
Phase D’s crate::adapter::net::redex::BandwidthClass surface adds dynamic
in-flight parallelism (~256 MB target). For TB-scale
streams on a fast link, the sequential path may not
saturate the wire; that’s an acknowledged Phase A
trade-off.
Sourcepub async fn publish_stream_with_downgrade(
&self,
stream: Pin<Box<dyn Stream<Item = Result<Bytes, BlobError>> + Send>>,
encoding: Encoding,
chunking: ChunkingStrategy,
size_hint: Option<u64>,
probes: &DowngradeProbes<'_>,
) -> Result<BlobRef, BlobError>
pub async fn publish_stream_with_downgrade( &self, stream: Pin<Box<dyn Stream<Item = Result<Bytes, BlobError>> + Send>>, encoding: Encoding, chunking: ChunkingStrategy, size_hint: Option<u64>, probes: &DowngradeProbes<'_>, ) -> Result<BlobRef, BlobError>
Publish a byte stream, choosing
BlobRef::Tree vs BlobRef::Manifest based on a
TreeSupportProbe + the TREE_THRESHOLD_BYTES
producer hint, AND applying CDC + erasure downgrades from
the matching capability probes before any store work runs.
Decision flow:
- Apply
super::cdc::cdc_downgradetochunking— peers that don’t advertise CDC support get theFixedfallback so their chunk-store can recompute leaf boundaries. - Apply
super::erasure::erasure_downgradetoencoding— peers that don’t advertise Reed-Solomon support getReplicatedso they don’t see a stripe layout they can’t reconstruct. - If
tree_probe.check() == false, force the Manifest path (Tree-incompatible peer). Caps at 16 GiB; oversize streams returnBlobError::Backend. - Else if
size_hint < TREE_THRESHOLD_BYTES, prefer the Manifest path for round-trip efficiency. - Else use the Tree path with the (possibly downgraded) encoding + chunking.
size_hint is the producer’s best estimate of total
bytes — None defaults to “above threshold,” routing
the stream through Tree. The decision is one-way: a
stream routed to Manifest can’t switch to Tree
mid-stream because Manifest’s buffered path has already
committed to in-memory accumulation.
Phase A6: the TreeSupportProbe::Dynamic arm wires
future capability-tag advertisement; v0.3a callers
without that substrate use AlwaysSupported for
single-cluster deployments or ForceManifest for
cross-version cluster rollouts. The CDC + erasure
probes mirror the same shape one-for-one.
Sourcepub fn chunk_channel_for_hash(hash: &[u8; 32]) -> ChannelName
pub fn chunk_channel_for_hash(hash: &[u8; 32]) -> ChannelName
Channel name for a given chunk hash. Public accessor so
e2e tests + operator tools can construct chunk channels for
Redex::open_file / replication_coordinator_for lookups
without re-implementing the dataforts/blob/<hex32> format
(and risking drift).
Sourcepub async fn repair_blob(
&self,
blob_ref: &BlobRef,
) -> Result<RepairReport, BlobError>
pub async fn repair_blob( &self, blob_ref: &BlobRef, ) -> Result<RepairReport, BlobError>
Operator-driven Reed-Solomon repair sweep over the chunks
reachable from blob_ref. Walks the manifest tree,
inspects each ErasureLeaf stripe, and for any RS stripe
that has at least one missing data chunk:
- Fetch every surviving chunk (data + parity) of the stripe.
- If
>= kshards survive, run RS reconstruction. - Re-store each previously-missing data chunk under its original content-addressed hash.
Stripes that are already healthy (every data chunk present)
are skipped without I/O on the parity side. Stripes that
have fewer than k survivors are counted as unrecoverable
— repair_blob does NOT error on unrecoverable stripes;
it records them in the report so the operator can take
human action (restore from snapshot, accept data loss,
etc.). A single unrecoverable stripe doesn’t abort repair
of the rest of the blob.
Encoding::Replicated stripes (the small-stripe trailing
fallback) have no parity model and are skipped with a
dedicated counter.
Non-Tree blobs return a zero-counter report (no repair surface — Small and Manifest blobs have no parity).
The repair sweep is iterative (no concurrency for v0.3 Phase C7); a future commit may parallelise the per-stripe recovery across the BandwidthClass-aware send queue.
Trust model. This entry point is unauthenticated and
intended for system-internal callers: the operator CLI
running against a local store, an in-process scheduled
repair cadence (if one ever lands), and unit tests. A peer-
initiated / network-exposed repair must route through
Self::repair_blob_authorized instead, because the sweep
walks every chunk of the blob (full disk + CPU cost) and is
trivially amplifiable into a DoS by an attacker who can
reach this surface without a capability check.
Capability-gated wrapper around Self::repair_blob.
Mirrors the Self::pin_authorized / Self::unpin_authorized
/ Self::delete_chunk_authorized pattern: the adapter must
have an AuthGuard configured, and the caller must be
authorized for (origin_hash, channel) per
auth_allows_blob_op. Returns BlobError::Unauthorized
on either failure.
This is the peer-initiated / network-exposed repair entry.
repair_blob walks the entire tree, fetches every chunk,
hashes each, constructs an RS encoder per stripe, and may
re-store reconstructed bytes — a hostile caller running it
across many blobs amplifies I/O and CPU substantially, so it
must not be reachable without the capability check.
Source§impl MeshBlobAdapter
impl MeshBlobAdapter
Sourcepub async fn sync_blob(&self, blob_ref: &BlobRef) -> Result<(), BlobError>
pub async fn sync_blob(&self, blob_ref: &BlobRef) -> Result<(), BlobError>
Flush every chunk file referenced by blob_ref to disk.
Used by publish_with_blob (see
super::publish_with_blob) under
BlobDurability::DurableOnLocal
to satisfy “blob survives local node restart” before the
publish step. No-op for BestEffort; ReplicatedTo(n)
composes this with a wait-for-replicas poll above.
Iterates BlobRef::Small as a single chunk; iterates
BlobRef::Manifest over every ChunkRef. Each chunk’s
underlying RedexFile::sync runs sequentially — the call
order is stable but partial-progress on error means some
chunks may have been flushed before the failure point.
Surface as BlobError::Backend for the operator to
retry / inspect.
Trait Implementations§
Source§impl BlobAdapter for MeshBlobAdapter
impl BlobAdapter for MeshBlobAdapter
Source§fn prefetch<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<(), BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
fn prefetch<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<(), BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
Open each chunk channel against the local
Redex handle using
the adapter’s existing chunk_file_config. When
replication is configured + active on the underlying
handle, the per-channel runtime spawned by open_file
begins syncing from peers carrying the chunk’s
causal:<hex> advertisement — that’s the cross-node fetch
path. Returns Ok(()) as soon as every chunk channel has
been opened; the actual chunk arrival is asynchronous and
reachable via fetch / exists once the
replication-runtime sync completes.
No-op when the chunk is already locally present (the
open_file fast path on the existing entry skips the
spawn; the chunk-file len() check on a subsequent
fetch returns the bytes without going over the network).
Source§fn adapter_id(&self) -> &str
fn adapter_id(&self) -> &str
Source§fn accepted_schemes(&self) -> &[&str]
fn accepted_schemes(&self) -> &[&str]
BlobRefs.
The substrate’s blob-dispatch layer routes by channel-
configured blob_adapter_id; before invoking the adapter
it checks the inbound URI’s scheme against this list and
rejects with BlobError::UnsupportedScheme when the URI
scheme isn’t accepted. Default returns an empty slice,
which means “accept anything” — adapters in trusted /
single-tenant deployments may leave this as-is, but
adapters that have authority over a privileged backend
(FS adapter, host-side keys, etc.) should override and
list the schemes they actually serve so a publisher with
append rights cannot dictate arbitrary URIs the adapter
then resolves.Source§fn store<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
bytes: &'life2 [u8],
) -> Pin<Box<dyn Future<Output = Result<(), BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
MeshBlobAdapter: 'async_trait,
fn store<'life0, 'life1, 'life2, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
bytes: &'life2 [u8],
) -> Pin<Box<dyn Future<Output = Result<(), BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
'life2: 'async_trait,
MeshBlobAdapter: 'async_trait,
bytes at the URI carried in blob_ref. Most
adapters will derive the URI from blob_ref.hash (content-
addressing) and ignore the inbound URI; some (e.g.
FileSystemAdapter) honor it directly. The hash on
blob_ref is the source of truth — the substrate computes
it before calling this method.Source§fn fetch<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<Bytes, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
fn fetch<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<Bytes, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
blob_ref.uri. The substrate
runs BlobRef::verify on the returned bytes; on a
mismatch the call as a whole fails with
BlobError::HashMismatch. Read moreSource§fn fetch_range<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
range: Range<u64>,
) -> Pin<Box<dyn Future<Output = Result<Bytes, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
fn fetch_range<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
range: Range<u64>,
) -> Pin<Box<dyn Future<Output = Result<Bytes, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
range.start <= range.end and both
bounded by blob_ref.size; out-of-range queries surface as
BlobError::Backend from the adapter. The substrate does
NOT verify partial fetches against the full-content hash;
callers using range fetch are accepting that trade-off. Read moreSource§fn exists<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<bool, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
fn exists<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<bool, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
fetch + drop; the trait
makes no efficiency promise.Source§fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
_blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<(), BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
fn delete<'life0, 'life1, 'async_trait>(
&'life0 self,
_blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<(), BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
MeshBlobAdapter); external-storage
adapters (S3 / IPFS) typically defer durability decisions
to the backend’s own lifecycle policies and may treat this
as a no-op. Read moreSource§fn stat<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<BlobStat, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
fn stat<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<BlobStat, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
net blob stat CLI + the metrics exporters; surfaces size,
replica counts (where the adapter knows), encoding, etc. Read moreSource§fn list<'life0, 'life1, 'async_trait>(
&'life0 self,
opts: &'life1 BlobListOptions,
) -> Pin<Box<dyn Future<Output = Result<Vec<BlobInventoryEntry>, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
fn list<'life0, 'life1, 'async_trait>(
&'life0 self,
opts: &'life1 BlobListOptions,
) -> Pin<Box<dyn Future<Output = Result<Vec<BlobInventoryEntry>, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
MeshBlobAdapter: 'async_trait,
DECK_PLAN.md § Deferred work § Blob & Artifact
Explorer) — adapters that can cheaply enumerate (Mesh,
fs) override; adapters with prohibitive enumeration
cost (S3 with millions of keys, IPFS) leave the default
“empty” so consumers don’t accidentally rack up backend
charges. Read moreSource§fn supports_list(&self) -> bool
fn supports_list(&self) -> bool
Self::list returns an authoritative enumeration. Read moreSource§fn fetch_stream<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<Pin<Box<dyn Stream<Item = Result<Bytes, BlobError>> + Send>>, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn fetch_stream<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
) -> Pin<Box<dyn Future<Output = Result<Pin<Box<dyn Stream<Item = Result<Bytes, BlobError>> + Send>>, BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Self::fetch and emits the
whole payload as a single chunk — fine for adapters that
hold blobs in RAM or pull them in one shot anyway (S3
GetObject with no Range, IPFS). Adapters with real
streaming backends (chunked HTTP, mmap’d local files,
range-fetched S3) should override to yield progressively. Read moreSource§fn store_stream<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
stream: Pin<Box<dyn Stream<Item = Result<Bytes, BlobError>> + Send>>,
size_hint: Option<u64>,
) -> Pin<Box<dyn Future<Output = Result<(), BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
fn store_stream<'life0, 'life1, 'async_trait>(
&'life0 self,
blob_ref: &'life1 BlobRef,
stream: Pin<Box<dyn Stream<Item = Result<Bytes, BlobError>> + Send>>,
size_hint: Option<u64>,
) -> Pin<Box<dyn Future<Output = Result<(), BlobError>> + Send + 'async_trait>>where
'life0: 'async_trait,
'life1: 'async_trait,
Self: 'async_trait,
Vec<u8> and forwards to Self::store;
adapters with real streaming write paths (S3 multipart
upload, chunked filesystem write) should override. Read moreSource§impl Clone for MeshBlobAdapter
impl Clone for MeshBlobAdapter
Source§fn clone(&self) -> MeshBlobAdapter
fn clone(&self) -> MeshBlobAdapter
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more