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 ¶
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 ¶
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 (*Guard) Admit ¶
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 ¶
Err returns why the medium cannot take writes, or nil while it can. The error wraps backend.ErrNoSpace.
func (*Guard) Observe ¶
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 ¶
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 ¶
TakeRejections returns the writes refused since the previous call and resets the count, so the caller that publishes the state also publishes its cost.