diskio

package
v0.18.40 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 5, 2026 License: AGPL-3.0 Imports: 4 Imported by: 0

Documentation

Overview

Package diskio bounds the page-cache impact of the worker's large sequential file writes (operator spill files, local cache downloads, stage-output files).

Mechanism: windowed asynchronous writeback (PostgreSQL's checkpoint_flush_after, RocksDB's bytes_per_sync — their DEFAULT, non-strict shapes). As each window of windowBytes completes, asynchronous writeback is started for it (sync_file_range(SYNC_FILE_RANGE_WRITE)), so dirty pages reach the device within one window of being written instead of waiting on the ~30s kernel flusher. Steady-state dirty footprint per writer ≈ write-rate × device-latency. There is deliberately no per-window blocking wait — the strict variant (RocksDB's off-by-default strict_bytes_per_sync) serialized multi-GB cache downloads behind device writeback at SF100 and regressed exchange-heavy queries 50-110% (run 20260610-203304); see Flusher.wrote.

Why dirty pages specifically: they cannot be reclaimed until written back, so a multi-GB write flood (cache downloads + spill) forces kernel reclaim to evict whatever IS cheaply reclaimable — the clean mmap'd cache-file pages concurrent tasks are still walking. That eviction is the major-fault mechanism behind the PR #112 regression (project_mmap_selfrelief_postmortem_2026-06-10); bounding the write side attacks the cause without any reader coordination.

Class picks the fate of written-back (now clean) windows:

  • Spill: read back at most once, much later — each clean window is FADV_DONTNEED'd immediately, so spill bytes never compete with the mmap'd cache pages at all. Read-back streams from NVMe.
  • KeepResident: mmap-walked or uploaded right after the write — the windows stay resident (only the dirty bound applies), so the imminent readers take minor faults, not majors.

The write mechanism is gated on --bounded-dirty-writes (default true since the SF100 suite-neutral validation; =false restores kernel-writeback-only), with Spill-class writers additionally kept active by the drop-behind default below. When fully disabled, NewWriter returns the file itself and a nil Flusher, so the only cost is one atomic load at writer construction.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddReadDropBytes

func AddReadDropBytes(n int64)

AddReadDropBytes records n bytes dropped behind a single-pass read by a caller that issues its own advise calls (the worker's shuffle mmap walk).

func DropBehindEnabled

func DropBehindEnabled() bool

DropBehindEnabled reports whether single-pass drop-behind is active.

func DropBehindStats

func DropBehindStats() (write, read int64)

DropBehindStats returns cumulative dropped bytes on the write and read sides since process start.

func Enabled

func Enabled() bool

Enabled reports whether windowed writeback is active.

func NewDropBehindReader

func NewDropBehindReader(f *os.File) io.Reader

NewDropBehindReader wraps f for drop-behind reading. Returns f itself when drop-behind is disabled, so call sites need no branches. The caller keeps ownership of f (Close as usual).

func SetDropBehindEnabled

func SetDropBehindEnabled(on bool)

SetDropBehindEnabled toggles single-pass drop-behind; tests only.

func SetEnabled

func SetEnabled(on bool)

SetEnabled activates or deactivates windowed writeback for writers created afterwards. Called once from worker startup (the --bounded-dirty-writes flag); tests may toggle it.

Types

type Class

type Class int

Class describes how a file's pages are treated once written back.

const (
	// Spill marks files written now and read back at most once, later:
	// sort/window runs, join build/probe partitions, aggregate partial
	// state, raw-row spill.
	Spill Class = iota
	// KeepResident marks files whose pages are wanted immediately after
	// the write: local cache downloads that are mmap'd and walked, and
	// stage outputs that are uploaded (and possibly adopted into the
	// LocalStageCache and mmap'd by a same-worker consumer) right after
	// Finalize.
	KeepResident
)

type DropBehindCursor

type DropBehindCursor struct {
	// contains filtered or unexported fields
}

DropBehindCursor is offset-driven drop-behind for readers that consume a single-pass file via out-of-band preads delivered in ascending order (the WSHF extent-index decode path — docs/design/shuffle-extent-index.md): the reads happen on worker goroutines, but delivery is strictly in order, so the delivery cursor is the safe drop watermark. Same window shape, counters, and advisory-failure latch as DropBehindReader.

func NewDropBehindCursor

func NewDropBehindCursor(f *os.File) *DropBehindCursor

NewDropBehindCursor returns a cursor for f, or nil when drop-behind is disabled. A nil cursor's Advance is a no-op, so call sites need no branches.

func (*DropBehindCursor) Advance

func (c *DropBehindCursor) Advance(off int64)

Advance marks everything below off consumed and drops completed windows behind it, keeping the window containing off resident.

type DropBehindReader

type DropBehindReader struct {
	// contains filtered or unexported fields
}

DropBehindReader reads a single-pass file sequentially and drops consumed pages from the page cache one window behind the read cursor, so multi-GB spill read-backs never displace reusable pages. On EOF the whole file is dropped (covers the tail window). Advisory only: FADV errors disable further calls but never affect the data read.

func (*DropBehindReader) Read

func (r *DropBehindReader) Read(p []byte) (int, error)

type Flusher

type Flusher struct {
	// contains filtered or unexported fields
}

Flusher applies the two-window writeback pattern to one sequentially written file. Not goroutine-safe: every write site already serializes writes per file (bufio behind a mutex, or a single goroutine).

func NewWriter

func NewWriter(f *os.File, class Class) (io.Writer, *Flusher)

NewWriter wraps f so windowed writeback tracks every Write. All sequential content writes must go through the returned writer; non-sequential header patches may go to f directly (Finish covers them with a whole-file pass). When the mechanism is disabled it returns f itself and a nil Flusher (whose Finish is a no-op), so call sites need no branches.

Spill-class writers are additionally active whenever drop-behind is on (the default), independent of --bounded-dirty-writes: a Spill file is single-pass by definition, so dropping its written-back windows is the steady-regime fix, not a memory-tight tuning knob. KeepResident writers (imminently re-read files) stay gated on the flag.

func (*Flusher) Finish

func (fl *Flusher) Finish()

Finish completes the page-cache treatment for the file. Call it after the final content write — including any header patch made directly on the file — and before the file is closed. For Spill files it writes back whatever is still dirty (the tail plus any patched header page) and drops the entire file from cache; KeepResident files keep their pages, so it is a no-op. Errors are swallowed: the machinery is advisory and never affects file contents. nil-safe.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL