pub struct SingleFileDirectory { /* private fields */ }Expand description
Production storage backend: all data in a single .luci file using the
block allocator and two-root-pointer atomic commit.
See [[architecture-storage-format]] for the full design and [[architecture-storage-format#Atomic Commit Protocol]] for the commit sequence.
§Crash Safety
At any point, both root pointers reference intact (non-overwritten) metadata blocks. A crash mid-commit is recovered by falling back to the still-valid root. Uncommitted segment writes are orphaned blocks that the allocator does not reference — they are harmlessly reclaimed on the next file open.
Implementations§
Source§impl SingleFileDirectory
impl SingleFileDirectory
Sourcepub fn create(path: impl AsRef<Path>) -> Result<Self>
pub fn create(path: impl AsRef<Path>) -> Result<Self>
Create a new .luci file at the given path.
Fails if a file already exists at path.
Sourcepub fn open(path: impl AsRef<Path>) -> Result<Self>
pub fn open(path: impl AsRef<Path>) -> Result<Self>
Open an existing .luci file, performing crash recovery.
Validates root pointers, loads metadata from the best available root, and reconstructs the block allocator. If the active root’s checksum is invalid (torn write), falls back to the inactive root and repairs the header.
See [[architecture-storage-format#Crash Recovery]].
Sourcepub fn file_handle(&self) -> Arc<File>
pub fn file_handle(&self) -> Arc<File>
Return a shared reference to the underlying file handle.
Used by the reader path to avoid opening a separate fd,
which would break fcntl lock semantics (closing any fd
releases all locks for the process on that inode).
See [[architecture-cross-process-locking#Critical Constraint]].
Sourcepub fn set_write_timeout(&mut self, timeout: Duration)
pub fn set_write_timeout(&mut self, timeout: Duration)
Set the timeout for acquiring the cross-process write lock.
Default: 5 seconds. If another process holds the write lock,
retries with exponential backoff until the timeout expires,
then returns WriterLocked.
Sourcepub fn open_from_handle(file: Arc<File>) -> Result<Self>
pub fn open_from_handle(file: Arc<File>) -> Result<Self>
Open a read-only view using an existing file handle.
Does not open a new fd or acquire any locks. The caller’s
process already holds the appropriate lock via the writer’s fd.
Used by refresh_reader() to reload segment data without
breaking fcntl lock semantics.
Sourcepub fn write_segment(
&mut self,
segment_id: SegmentId,
data: &[u8],
) -> Result<()>
pub fn write_segment( &mut self, segment_id: SegmentId, data: &[u8], ) -> Result<()>
Write segment data to allocated blocks.
The segment is not visible to readers until commit
is called. Data is written to disk immediately so that commit only
needs to write metadata and flip the header.
Automatically acquires RESERVED lock on first write (blocks other
writers). The lock is held until commit() completes.
§Errors
Returns an error if data is empty or the write fails.
Returns WriterLocked if another process holds RESERVED.
Sourcepub fn read_segment(&self, segment_id: SegmentId) -> Result<Vec<u8>>
pub fn read_segment(&self, segment_id: SegmentId) -> Result<Vec<u8>>
Read committed segment data by segment ID.
Only committed segments are visible. Returns LuciError::IndexNotFound
if the segment does not exist in the committed state.
Sourcepub fn commit(&mut self) -> Result<()>
pub fn commit(&mut self) -> Result<()>
Atomically commit all pending segment writes.
Implements the six-step atomic commit protocol from [[architecture-storage-format]]:
- Segment data already written to blocks (by
write_segment) - Serialize new metadata to a freshly allocated block (copy-on-write)
- Compute checksum, update the inactive root pointer
fsyncthe data file- Write the 4 KB header with the flipped active root flag
fsyncthe header
The old inactive root’s metadata block is freed and included in the new free list — but never overwritten until after this commit succeeds, so both roots remain valid at all times for crash recovery.
Sourcepub fn segments(&self) -> &[SegmentEntry]
pub fn segments(&self) -> &[SegmentEntry]
The currently committed segment entries.
Sourcepub fn generation(&self) -> u64
pub fn generation(&self) -> u64
The current commit generation.
Sourcepub fn set_user_metadata(&mut self, metadata: Vec<u8>)
pub fn set_user_metadata(&mut self, metadata: Vec<u8>)
Set opaque user metadata to be persisted on the next commit.
Sourcepub fn user_metadata(&self) -> &[u8] ⓘ
pub fn user_metadata(&self) -> &[u8] ⓘ
Get the persisted user metadata (empty if none).
Sourcepub fn free_block_count(&self) -> u64
pub fn free_block_count(&self) -> u64
Total number of free (reusable) blocks tracked by the allocator.
Exposed for integration testing (free-list reclamation validation).
Sourcepub fn total_blocks(&self) -> u64
pub fn total_blocks(&self) -> u64
Total number of data blocks the file spans.
Exposed for integration testing.
Sourcepub fn remove_segments(&mut self, segment_ids: &[SegmentId])
pub fn remove_segments(&mut self, segment_ids: &[SegmentId])
Mark segments for removal on the next commit.
Sourcepub fn write_vector_index(
&mut self,
field_id: FieldId,
data: &[u8],
) -> Result<()>
pub fn write_vector_index( &mut self, field_id: FieldId, data: &[u8], ) -> Result<()>
Write a per-field vector index. The bytes are written to a fresh
extent immediately; the entry becomes visible to readers on the
next commit(). If the field already had a committed entry, its
old extent is freed during commit.
Sourcepub fn read_vector_index(&self, field_id: FieldId) -> Result<Option<Vec<u8>>>
pub fn read_vector_index(&self, field_id: FieldId) -> Result<Option<Vec<u8>>>
Read committed vector-index bytes for field_id. Returns None
if no committed index exists for that field.
Sourcepub fn vector_index_fields(&self) -> Vec<FieldId>
pub fn vector_index_fields(&self) -> Vec<FieldId>
List the fields that have a committed vector index.
Sourcepub fn remove_vector_index(&mut self, field_id: FieldId)
pub fn remove_vector_index(&mut self, field_id: FieldId)
Mark the vector index for field_id for removal on next commit.
Trait Implementations§
Source§impl Storage for SingleFileDirectory
Available on Unix only.
impl Storage for SingleFileDirectory
fn write_segment(&mut self, segment_id: SegmentId, data: &[u8]) -> Result<()>
fn read_segment(&self, segment_id: SegmentId) -> Result<Vec<u8>>
fn commit(&mut self) -> Result<()>
fn segments(&self) -> &[SegmentEntry]
fn generation(&self) -> u64
fn set_user_metadata(&mut self, metadata: Vec<u8>)
fn user_metadata(&self) -> &[u8] ⓘ
Source§fn remove_segments(&mut self, segment_ids: &[SegmentId])
fn remove_segments(&mut self, segment_ids: &[SegmentId])
Source§fn write_vector_index(&mut self, field_id: FieldId, data: &[u8]) -> Result<()>
fn write_vector_index(&mut self, field_id: FieldId, data: &[u8]) -> Result<()>
field_id on next
commit. See [[global-vector-indices]].Source§fn read_vector_index(&self, field_id: FieldId) -> Result<Option<Vec<u8>>>
fn read_vector_index(&self, field_id: FieldId) -> Result<Option<Vec<u8>>>
None if no index exists for that field.Source§fn vector_index_fields(&self) -> Vec<FieldId>
fn vector_index_fields(&self) -> Vec<FieldId>
Source§fn remove_vector_index(&mut self, field_id: FieldId)
fn remove_vector_index(&mut self, field_id: FieldId)
field_id for removal on the next
commit. No-op if the field has no committed index.Source§fn set_write_timeout(&mut self, timeout: Duration)
fn set_write_timeout(&mut self, timeout: Duration)
Auto Trait Implementations§
impl Freeze for SingleFileDirectory
impl RefUnwindSafe for SingleFileDirectory
impl Send for SingleFileDirectory
impl Sync for SingleFileDirectory
impl Unpin for SingleFileDirectory
impl UnsafeUnpin for SingleFileDirectory
impl UnwindSafe for SingleFileDirectory
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self>
fn into_either(self, into_left: bool) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more