diskguard

package
v0.40.1 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package diskguard refuses a write the medium cannot store, instead of accepting it and losing it later.

A flush that runs out of disk or of inodes fails after the head has been detached, burns a part sequence, and leaves the head to keep growing behind it — so a node with a full disk manufactures its own damage for as long as the disk stays full, while still acking every write. The guard closes that loop: a flush asks whether the backend can take the part *before* writing it, and a failure latches, turning the engine's ingest path into a distinct, retryable error until a later flush finds room again.

Bytes and inodes are checked as independent axes. A filesystem with terabytes free can still fail every create once its inode table is exhausted, and a part is many small objects, so the two failures are equally likely and byte accounting cannot see the second one.

Index

Constants

View Source
const (
	DefaultReserveBytes  = 64 << 20
	DefaultReserveInodes = 1024
)

Defaults for the headroom left unused. They are small on purpose: the guard exists to catch a medium that is *actually* out, not to enforce a capacity policy, and a large default would turn a healthy small volume read-only. The headroom's job is only to leave a merge room for its output, since a merge must write before it can retire the inputs it frees.

Variables

This section is empty.

Functions

func IsNoSpace

func IsNoSpace(err error) bool

IsNoSpace reports whether err says the medium is out of room — the library's own backend.ErrNoSpace, or the ENOSPC a filesystem returns for both a full disk and an exhausted inode table.

Types

type Guard

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

Guard is one engine's disk-pressure latch. The zero value is usable and takes the default reserve. Safe for concurrent use.

func New

func New(r Reserve) *Guard

New returns a guard keeping r free beyond each write's own requirement.

func (*Guard) Admit

func (g *Guard) Admit(ctx context.Context, b backend.Backend, needBytes, needObjects int64) error

Admit reports whether b can take a write of needBytes across needObjects objects, and latches the verdict either way — so a passing check is also what clears a previous failure. A backend that cannot report an axis (backend.ErrSpaceUnknown) is unbounded on that axis, never failing.

A backend that reports neither axis therefore clears a latch set by Guard.Observe on the next attempt. That is the intended behavior and the only one available: nothing can say when such a medium has room again except writing to it, so the flush cadence is the retry, and a write that fails again re-latches immediately.

The returned error wraps backend.ErrNoSpace. A probe that fails for any other reason (a statfs that errored) is not a verdict: it is returned as-is and leaves the latch alone, because refusing writes on a broken probe is a worse failure than the one being guarded against.

func (*Guard) Err

func (g *Guard) Err() error

Err returns why the medium cannot take writes, or nil while it can. The error wraps backend.ErrNoSpace.

func (*Guard) Exhausted

func (g *Guard) Exhausted() bool

Exhausted reports whether the medium is currently refusing writes.

func (*Guard) Observe

func (g *Guard) Observe(err error)

Observe latches err when it is an out-of-space failure, so a write that raced past Guard.Admit and got ENOSPC from the medium itself closes the ingest path just as a failed check would. Any other error is left alone: a transient backend fault must not make the node read-only.

func (*Guard) Refuse

func (g *Guard) Refuse() error

Refuse is Guard.Err for the ingest path: it returns the same reason and counts one refused write, which Guard.TakeRejections later publishes.

func (*Guard) TakeRejections

func (g *Guard) TakeRejections() int64

TakeRejections returns the writes refused since the previous call and resets the count, so the caller that publishes the state also publishes its cost.

type Reserve

type Reserve struct {
	Bytes  int64
	Inodes int64
}

Reserve is the headroom a store keeps free beyond what a pending write needs. A zero field takes the default above; a negative one disables that axis.

Jump to

Keyboard shortcuts

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