pub struct FsBlobStore { /* private fields */ }Expand description
A BlobStore over <root>/<keyspace>/<64-hex> for packs (packs by
default), <root>/upload-markers/v1/<64-hex> for upload markers,
<root>/objects/<64-hex> for extracted objects and
<root>/object-offsets/v1/<64-hex> for their offset sidecars.
An upload streams into a temp file in its destination
directory (named like FileTransport’s own, .<hex>.tmp.<pid>.<seq>)
while hashing it, and becomes visible only once its BLAKE3 and length
verify: fsync, rename over the destination, fsync the directory. A
failed, aborted or dropped upload removes its temp file and never
touches an existing blob. A new directory’s entry is fsynced into its
parent before anything is published in it. A full disk or quota is
StoreError::Full.
A process that crashes mid-upload leaves its temp file,
<keyspace>/.<64-hex>.tmp.<pid>.<seq> or a corresponding marker temp
file, behind. Neither is visible as a blob;
FsBlobStore::sweep_stale_uploads removes old ones.
Implementations§
Source§impl FsBlobStore
impl FsBlobStore
Sourcepub fn new(root: impl Into<PathBuf>) -> Self
pub fn new(root: impl Into<PathBuf>) -> Self
The packs keyspace under root, the directory FileTransport
uploads to.
Sourcepub fn with_keyspace(root: impl Into<PathBuf>, keyspace: &'static str) -> Self
pub fn with_keyspace(root: impl Into<PathBuf>, keyspace: &'static str) -> Self
The keyspace directory under root. keyspace is one plain path
component; objects, object-offsets and upload-markers are the
sibling namespaces’ own directories, so a pack keyspace cannot use
them (its keys are refused).
§Panics
If keyspace is empty, starts with . or holds a path separator.
Sourcepub fn sweep_stale_uploads(&self, min_age: Duration) -> Result<usize>
pub fn sweep_stale_uploads(&self, min_age: Duration) -> Result<usize>
Remove temp files crashed uploads left in the pack and marker
directories: regular files named exactly .<64-hex>.tmp.<pid>.<seq>
(the names temp_path gives an upload, from this store or
FileTransport::upload_pack) last modified at least min_age ago.
Nothing else is touched: no blob, no symlink, no other name, no file
modified in the future. It also removes server-uploads session
directories whose immutable meta file’s mtime is at least seven days
plus one hour old, independently of min_age. An incomplete session
without meta uses the directory mtime. Returns how many entries
were removed; an entry that cannot be inspected or removed is skipped.
A live upload keeps its temp file’s modification time fresh as it
writes, so a min_age well above any pause between two writes of
one upload is safe against FileTransport writers, which take no
lock. The caller must also rule out a live writer that can pause for
longer, a stalled streaming upload: mkit serve sweeps only while
it holds serve.lock exclusively, which no other mkit serve or
mkit-server (each holds it shared) can then hold.
§Errors
I/O listing the directory; a missing directory sweeps nothing.
Trait Implementations§
Source§impl BlobStore for FsBlobStore
impl BlobStore for FsBlobStore
Source§type Sink = FsPackSink
type Sink = FsPackSink
Self::begin returns.Source§async fn begin(&self, key: BlobKey, len: u64) -> Result<FsPackSink, StoreError>
async fn begin(&self, key: BlobKey, len: u64) -> Result<FsPackSink, StoreError>
key of len bytes.Source§async fn get(
&self,
key: &BlobKey,
range: Option<ByteRange>,
) -> Result<Option<BlobBody>, StoreError>
async fn get( &self, key: &BlobKey, range: Option<ByteRange>, ) -> Result<Option<BlobBody>, StoreError>
range of them. A body longer than
MAX_BLOB_PIECE_BYTES MUST be a BlobBody::Stream whose pieces
are each at most MAX_BLOB_PIECE_BYTES (an adapter re-chunks its
backend’s stream); a backend never buffers a whole pack. A range
starting at or past the end is StoreError::RangeNotSatisfiable.Source§impl Clone for FsBlobStore
impl Clone for FsBlobStore
Source§impl Debug for FsBlobStore
impl Debug for FsBlobStore
Source§impl MultipartBlobStore for FsBlobStore
impl MultipartBlobStore for FsBlobStore
Source§type PartSink = FsPartSink
type PartSink = FsPartSink
Self::begin_part.Source§fn supports_multipart(&self) -> bool
fn supports_multipart(&self) -> bool
Source§async fn begin_multipart(
&self,
key: BlobKey,
len: u64,
part_size: u64,
) -> Result<Vec<u8>, StoreError>
async fn begin_multipart( &self, key: BlobKey, len: u64, part_size: u64, ) -> Result<Vec<u8>, StoreError>
Source§async fn begin_multipart_for_ticket(
&self,
key: BlobKey,
len: u64,
part_size: u64,
ticket_id: [u8; 32],
) -> Result<Vec<u8>, StoreError>
async fn begin_multipart_for_ticket( &self, key: BlobKey, len: u64, part_size: u64, ticket_id: [u8; 32], ) -> Result<Vec<u8>, StoreError>
Source§async fn begin_part(
&self,
key: BlobKey,
session: &[u8],
plan: &PartPlan,
index: u32,
expected_cv: [u8; 32],
) -> Result<FsPartSink, StoreError>
async fn begin_part( &self, key: BlobKey, session: &[u8], plan: &PartPlan, index: u32, expected_cv: [u8; 32], ) -> Result<FsPartSink, StoreError>
Source§async fn complete(
&self,
key: BlobKey,
session: &[u8],
plan: &PartPlan,
parts: &[PartRef],
) -> Result<CommitOutcome, StoreError>
async fn complete( &self, key: BlobKey, session: &[u8], plan: &PartPlan, parts: &[PartRef], ) -> Result<CommitOutcome, StoreError>
Source§async fn complete_with_root(
&self,
key: BlobKey,
session: &[u8],
plan: &PartPlan,
parts: &[PartRef],
content_root: Hash,
) -> Result<CommitOutcome, StoreError>
async fn complete_with_root( &self, key: BlobKey, session: &[u8], plan: &PartPlan, parts: &[PartRef], content_root: Hash, ) -> Result<CommitOutcome, StoreError>
content_root (the object key is an object id, not a
content hash). Plain Self::complete refuses object keys, as
PackSink::commit does. WP-4.10.Source§async fn abort(&self, key: BlobKey, session: &[u8]) -> Result<(), StoreError>
async fn abort(&self, key: BlobKey, session: &[u8]) -> Result<(), StoreError>
Source§fn single_put_limit(&self) -> Option<u64>
fn single_put_limit(&self) -> Option<u64>
BlobStore::begin upload can carry, or None
when unbounded. Objects longer than this are extracted in parts.