pub struct FsStore { /* private fields */ }Expand description
Filesystem-based storage for Heddle objects.
Layout:
.heddle/
objects/
blobs/
ab/
cdef1234...
trees/
ab/
cdef1234...
states/
<change_id>.state
actions/
<action_id>.action
packs/
<hash>.pack
<hash>.idxImplementations§
Source§impl FsStore
impl FsStore
Sourcepub fn new(root: impl AsRef<Path>) -> Self
pub fn new(root: impl AsRef<Path>) -> Self
Create a new filesystem store rooted at the given path.
The path should be the .heddle directory.
Sourcepub fn with_compression(
root: impl AsRef<Path>,
compression: CompressionConfig,
) -> Self
pub fn with_compression( root: impl AsRef<Path>, compression: CompressionConfig, ) -> Self
Create a new filesystem store with custom compression settings.
Sourcepub fn compression(&self) -> CompressionConfig
pub fn compression(&self) -> CompressionConfig
Get the compression configuration.
Sourcepub fn set_compression(&mut self, compression: CompressionConfig)
pub fn set_compression(&mut self, compression: CompressionConfig)
Set the compression configuration.
pub fn loose_object_write_mode(&self) -> LooseObjectWriteMode
pub fn set_loose_object_write_mode(&mut self, mode: LooseObjectWriteMode)
Sourcepub fn reload_packs(&self) -> Result<()>
pub fn reload_packs(&self) -> Result<()>
Reload pack files from disk.
Sourcepub fn pack_manager(&self) -> &RwLock<PackManager>
pub fn pack_manager(&self) -> &RwLock<PackManager>
Get the pack manager for pack operations.
pub fn clear_recent_object_caches(&self)
pub fn pack_ids(&self) -> Result<Vec<PackObjectId>>
Trait Implementations§
Source§impl ObjectStore for FsStore
impl ObjectStore for FsStore
Source§fn get_blob_bytes(&self, hash: &ContentHash) -> Result<Option<Bytes>>
fn get_blob_bytes(&self, hash: &ContentHash) -> Result<Option<Bytes>>
Zero-copy pack fast path. When the blob lives in a packfile
and is non-delta + uncompressed, returns a Bytes::slice
view of the pack’s mmap — no decompression, no allocation,
no memcpy. Compressed pack entries, delta entries, and
loose blobs fall back to get_blob and wrap the result in a
Bytes (the Vec → Bytes conversion is itself zero-copy).
Source§fn loose_blob_path(&self, hash: &ContentHash) -> Option<PathBuf>
fn loose_blob_path(&self, hash: &ContentHash) -> Option<PathBuf>
Loose blob path safe for clonefile/copy materialization.
Returns Some(path) only when the loose file exists, is
stored uncompressed, and its bytes hash to the expected
content hash. Compressed blobs and pack-only blobs fall
through to None; so do torn cache-mirror files (the
AtomicWriteMode::NoSync write side may leave one if the
host crashed during a previous promote). On the torn case
the caller re-promotes from the authoritative pack copy.
Verification is amortised: a hash that passes the check once
in this process is recorded in verified_loose_blobs and
subsequent calls skip the read+hash. So the cost on the
materialize hot path is at most one BLAKE3 over each unique
blob per process lifetime — negligible for tiny blobs,
bounded by working-set size for huge ones.
Source§fn promote_to_loose_uncompressed(&self, hash: &ContentHash) -> Result<bool>
fn promote_to_loose_uncompressed(&self, hash: &ContentHash) -> Result<bool>
Promote a blob to its uncompressed-loose canonical path so
loose_blob_path returns Some(path) and hardlink-first
materialization fires.
Three cases:
- Already loose+uncompressed: peek the header, no-op.
- Loose but compressed: read+decompress, atomically rewrite the canonical path with raw bytes.
- Pack-only: read out of the pack via
get_blob, atomically write to the canonical loose path. Pack copy is left in place — the next prune cycle will discard the loose mirror and a future materialize will re-promote.
Source§fn clear_recent_caches(&self)
fn clear_recent_caches(&self)
InMemoryStore is already the source of truth). Read morefn get_blob(&self, hash: &ContentHash) -> Result<Option<Blob>>
fn put_blob(&self, blob: &Blob) -> Result<ContentHash>
fn put_blob_with_hash( &self, blob: &Blob, hash: ContentHash, ) -> Result<ContentHash>
fn put_blob_bytes_with_hash( &self, data: &[u8], hash: ContentHash, ) -> Result<ContentHash>
fn has_blob(&self, hash: &ContentHash) -> Result<bool>
Source§fn blob_size(&self, hash: &ContentHash) -> Result<Option<u64>>
fn blob_size(&self, hash: &ContentHash) -> Result<Option<u64>>
hash, or Ok(None) when the blob is not in the store. Read morefn get_tree(&self, hash: &ContentHash) -> Result<Option<Tree>>
fn put_tree(&self, tree: &Tree) -> Result<ContentHash>
fn put_tree_serialized( &self, data: &[u8], hash: ContentHash, ) -> Result<ContentHash>
fn has_tree(&self, hash: &ContentHash) -> Result<bool>
fn get_state(&self, id: &ChangeId) -> Result<Option<State>>
fn put_state(&self, state: &State) -> Result<()>
fn put_state_serialized(&self, data: &[u8], id: ChangeId) -> Result<()>
fn has_state(&self, id: &ChangeId) -> Result<bool>
fn list_states(&self) -> Result<Vec<ChangeId>>
fn get_action(&self, id: &ActionId) -> Result<Option<Action>>
fn put_action(&self, action: &mut Action) -> Result<ActionId>
fn list_actions(&self) -> Result<Vec<ActionId>>
fn list_blobs(&self) -> Result<Vec<ContentHash>>
fn list_trees(&self) -> Result<Vec<ContentHash>>
fn pack_objects(&self, aggressive: bool) -> Result<(u64, u64)>
fn get_pack_object( &self, id: &PackObjectId, ) -> Result<Option<(ObjectType, Vec<u8>)>>
fn install_pack( &self, pack_data: &[u8], index_data: &[u8], ) -> Result<Vec<PackObjectId>>
Source§fn put_blobs_packed(&self, blobs: Vec<(ContentHash, Vec<u8>)>) -> Result<()>
fn put_blobs_packed(&self, blobs: Vec<(ContentHash, Vec<u8>)>) -> Result<()>
FsStore) override this to install one
packfile + index — two fsyncs total instead of N. Used by the
snapshot hot path so writing 1000 small files takes ~one fsync,
not 1000. Read moreSource§fn install_pack_streaming(
&self,
pack_path: &Path,
index_path: &Path,
) -> Result<Vec<PackObjectId>>
fn install_pack_streaming( &self, pack_path: &Path, index_path: &Path, ) -> Result<Vec<PackObjectId>>
StreamingPackBuilder). The default
impl reads both files fully and delegates to install_pack,
so any backend that doesn’t override this still works (at the
cost of giving back the bounded-memory promise). Real fs-
backed stores override this to rename(2) both files into the
pack directory without ever loading them. Read morefn prune_loose_objects(&self) -> Result<(u64, u64)>
fn begin_snapshot_write_batch(&self) -> Result<()>
fn flush_snapshot_write_batch(&self) -> Result<()>
fn abort_snapshot_write_batch(&self)
Source§fn has_redactions_for_blob(&self, blob: &ContentHash) -> Result<bool>
fn has_redactions_for_blob(&self, blob: &ContentHash) -> Result<bool>
Source§fn get_redactions_bytes_for_blob(
&self,
blob: &ContentHash,
) -> Result<Option<Vec<u8>>>
fn get_redactions_bytes_for_blob( &self, blob: &ContentHash, ) -> Result<Option<Vec<u8>>>
RedactionsBlob bytes for the given
blob, or Ok(None) if no redaction record exists. The bytes
are byte-identical to what was written by put_redactions_bytes_for_blob
(or by Repository::put_redaction); this is the wire-transfer
payload, not a re-serialized view. Read moreSource§fn put_redactions_bytes_for_blob(
&self,
blob: &ContentHash,
bytes: &[u8],
) -> Result<()>
fn put_redactions_bytes_for_blob( &self, blob: &ContentHash, bytes: &[u8], ) -> Result<()>
RedactionsBlob bytes for the given
blob. Receiver-side replay calls this after signature
verification so the bytes land in the same sidecar that the
sender’s Repository::put_redaction writes to. Read moreSource§fn list_blobs_with_redactions(&self) -> Result<Vec<ContentHash>>
fn list_blobs_with_redactions(&self) -> Result<Vec<ContentHash>>
Source§fn has_state_visibility_for_state(&self, state: &ChangeId) -> Result<bool>
fn has_state_visibility_for_state(&self, state: &ChangeId) -> Result<bool>
state. Read moreSource§fn get_state_visibility_bytes_for_state(
&self,
state: &ChangeId,
) -> Result<Option<Vec<u8>>>
fn get_state_visibility_bytes_for_state( &self, state: &ChangeId, ) -> Result<Option<Vec<u8>>>
StateVisibilityBlob bytes for state,
or Ok(None) if no sidecar exists. The bytes are the wire-transfer
payload for state visibility. Read more