Documentation
¶
Overview ¶
Package fileio is mlfs's file data path: it turns POSIX read/write at byte offsets into slice writes (chunk store) + slice records (meta engine), and resolves overlapping slices on read so newer writes win.
A file is a sequence of fixed-size chunks (meta.ChunkSize). A Write produces one new slice per touched chunk, whose bytes go to the chunk store under a freshly-allocated slice id; a slice_ref row records where it lands. A Read gathers a chunk's slice history (write order) and overlays them — the overlaid slice ranges: the newest slice wins, uncovered ranges read as zeros (holes). Copy-on-write (Clone) is a pure metadata slice-row copy, sharing the underlying chunks.
Index ¶
- type Files
- func (f *Files) Clone(ctx context.Context, src, dst meta.Ino) error
- func (f *Files) Close() error
- func (f *Files) CopyFileRange(ctx context.Context, src, dst meta.Ino, srcOff, dstOff int64, length int) (int, error)
- func (f *Files) EnableReadahead(window int64, concurrency int, m *metrics.Registry)
- func (f *Files) HashFile(ctx context.Context, ino meta.Ino) (string, error)
- func (f *Files) Read(ctx context.Context, ino meta.Ino, offset int64, buf []byte) (int, error)
- func (f *Files) Truncate(ctx context.Context, ino meta.Ino, size int64) error
- func (f *Files) Write(ctx context.Context, ino meta.Ino, offset int64, data []byte, ...) (int, error)
- type ReplayResult
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Files ¶
type Files struct {
// contains filtered or unexported fields
}
Files is the data path over a metadata engine + chunk store.
func New ¶
func New(m *meta.Engine, chunks chunkstore.Store) *Files
New constructs the data path with no write-ahead log (crash recovery of uncommitted metadata writes is then unavailable — used by tests and tools).
func NewWithWAL ¶
NewWithWAL constructs the data path backed by a local data WAL: each chunk write is logged (fsync'd) AFTER the data is staged but BEFORE the metadata commit, so a crash in that window is recovered by ReplayWAL on the next start instead of losing an acknowledged write. Pass the same WAL to ReplayWAL at startup before serving.
func (*Files) Close ¶
Close stops the background readahead prefetcher (if enabled) and waits for in-flight prefetches to finish. Safe to call when readahead was never enabled.
func (*Files) CopyFileRange ¶
func (f *Files) CopyFileRange(ctx context.Context, src, dst meta.Ino, srcOff, dstOff int64, length int) (int, error)
CopyFileRange copies length bytes at srcOff in src to dstOff in dst, as copy_file_range(2): it returns the number of bytes copied, clamped to src's length. When the whole copied run is chunk-aligned (both offsets and the run length land on meta.ChunkSize boundaries, the common case for a streaming cp(1)) it shares the underlying chunks copy-on-write — zero data movement, the same blob-sharing as Clone. Any non-aligned head/tail (mid-chunk offsets or a final partial chunk) falls back to a plain read+write copy, so the result is always byte-correct regardless of alignment.
func (*Files) EnableReadahead ¶
EnableReadahead turns on sequential-read prefetch: on a detected sequential stream, window bytes ahead of the read cursor are proactively materialized into the disk cache, hiding the per-slice backing (S3) + metadata (PG) round trips the kernel's ≤128 KiB FUSE readahead can't cover (e.g. cold model loading). concurrency bounds in-flight backing fetches (≤0 = default); on a cold sequential read this is the throughput knob (aggregate ≈ concurrency × per-slice rate), so scale it with the metadata pool on read-heavy pools. window ≤ 0 disables it; metrics may be nil. Call once before serving and pair with Close to stop the background prefetcher.
func (*Files) HashFile ¶
HashFile computes the whole-file sha256 of ino as lowercase hex (no algorithm prefix), matching blobgw's ContentHash encoding. It streams the file's bytes in order through the normal Read path, so it resolves overlapping slices, reads holes as zeros, and works over either the disk cache or the casstore backing — giving the correct digest regardless of how the writes arrived (sequential, random backfill, sparse). This is the recompute fallback the bridge uses when the incremental running hash was invalidated by a behind-watermark write; it is also the definition the incremental fast path must agree with. An empty file hashes to sha256("").
func (*Files) Read ¶
Read fills buf with the file's bytes starting at offset, returning the count (clamped to file length; holes read as zeros). Returns 0 at/after EOF.
func (*Files) Truncate ¶
Truncate sets the file length. Shrink is honored on read (bytes past the new length are not returned); reclaiming now-unreachable slice rows is a later refinement. Grow makes the tail sparse (reads as zeros).
func (*Files) Write ¶
func (f *Files) Write(ctx context.Context, ino meta.Ino, offset int64, data []byte, class chunkstore.StoreClass) (int, error)
Write writes data at byte offset in file ino, returning bytes written. It splits across chunk boundaries, allocating one slice per touched chunk. class is the file's compression class (caller-supplied; the bridge reads it from the inode), carried onto every slice so model/tensor files stay uncompressed and generic files compress.
type ReplayResult ¶
type ReplayResult struct {
Applied int // records (re-)committed to the metadata engine
Skipped int // records no longer applicable (already done, file gone, or fenced)
}
ReplayResult summarizes a WAL recovery pass.
func ReplayWAL ¶
ReplayWAL re-applies every logged write/truncate to the metadata engine on startup, recovering writes that were staged + logged but whose metadata commit was lost to a crash. It MUST run before the daemon serves, and after the ownership coordinator is registered so the fence (L2.8) governs replay.
meta.Write/Truncate are idempotent (ON CONFLICT on the slice_id natural key; truncate is set-length), so re-applying an already-committed record is a harmless no-op. Records that are no longer applicable are SKIPPED, not forced:
- ENOENT/EINVAL: the target was deleted or is no longer a regular file.
- ESTALE (multi-node): the scope was taken over by a peer that holds a newer generation — the conservative fenced-replay rule (docs/L2.8 §9): we do not resurrect a write over a peer's authoritative state.
Only a genuine I/O error (EIO) aborts recovery. On success the WAL is checkpointed so recovered segments are dropped.