WeaveDocs
Strand Blobs

Writer and Reader

Streaming BlobWriter and BlobReader APIs for Strand Blobs — chunked uploads, range reads, and integrity verification on every chunk.

Purpose

Chunked blob storage with deduplication, cache, writer/reader APIs, repair, metrics, and policy hooks.

This page follows the real source shape for Strand Blobs and explains the workflow a developer is likely to use first.

Developer workflow

Start from the smallest constructor or builder, perform one meaningful operation, inspect the returned state, then add the relevant policy, storage, or network integration. The examples below should be expanded whenever the crate API changes.

Tip

Stream large blobs through BlobWriter rather than buffering full files in memory. Writers are flushed and committed per chunk, so a crash midway loses only the in-flight chunk.

Primary types to know

  • AllowAllBlobPolicy — models/strand-blobs/src/policy.rs
  • BlobConfig — models/strand-blobs/src/config.rs
  • BlobEntry — models/strand-blobs/src/chunk.rs
  • BlobId — models/strand-blobs/src/blob.rs
  • BlobInfo — models/strand-blobs/src/blob.rs
  • BlobMetadata — models/strand-blobs/src/blob.rs
  • BlobMetrics — models/strand-blobs/src/metrics.rs
  • BlobMetricsSnapshot — models/strand-blobs/src/metrics.rs
  • BlobReader — models/strand-blobs/src/reader.rs
  • BlobStore — models/strand-blobs/src/store.rs
  • BlobWriter — models/strand-blobs/src/writer.rs
  • ChunkCache — models/strand-blobs/src/cache.rs

Example shape

use strand::{Strand, StrandConfig};
use strand_blobs::{BlobConfig, BlobStore};
use tokio::io::AsyncReadExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let dir = tempfile::tempdir()?;
    let meta = Strand::new(StrandConfig::new().with_storage(dir.path().join("meta"))).await?;
    let data = Strand::new(StrandConfig::new().with_storage(dir.path().join("data"))).await?;
    let store = BlobStore::new(meta, data, BlobConfig::default()).await?;

    // create_writer returns a streaming BlobWriter. Buffer-by-buffer puts let
    // callers stream multi-GB blobs without holding the payload in memory.
    let metadata = serde_json::json!({ "kind": "log", "rotation": "daily" });
    let mut writer = store.create_writer(Some(metadata));
    for chunk in [&b"line one\n"[..], &b"line two\n"[..], &b"line three\n"[..]] {
        writer.write_chunk(chunk).await?;
    }
    let blob_id = writer.finish().await?;
    let bytes_written = writer.bytes_written();
    let chunks_written = writer.chunks_written();

    // BlobReader streams the same data back with bounded buffers.
    let mut reader = store.get(&blob_id).await?.expect("blob present");
    let mut readback = Vec::with_capacity(reader.size() as usize);
    reader.read_to_end(&mut readback).await?;

    println!(
        "wrote {bytes_written} bytes across {chunks_written} chunks; readback={} bytes",
        readback.len(),
    );
    Ok(())
}