Quota: maxTotalBytes measures the whole filesystem, and quota.enabled is ignored #497

Closed
opened 2026-09-11 21:04:09 +00:00 by Cordy · 1 comment
Owner

Status: from source (read at bd006ef, not reproduced). Line numbers at bd006ef.

What happens

  • maxTotalBytes is checked as usage.Used >= instanceTotal (internal/storage/quota/quota.go:113–119). On POSIX, Used is the whole filesystem's statfs usage, (Blocks − Bfree) × Bsize (internal/storage/posix/usage_unix.go:16–26) — not what is stored under the storage root.
  • storage.quota.enabled only gates a log line; the quota decorator is always installed with whatever limits are set (cmd/cairnd/main.go:578–595 at bd006ef).

Impact

On a volume shared with anything else (the OS disk, other services, a NAS), maxTotalBytes either blocks every write from the start or never triggers. cairn-desktop's integration tests had to use perUserBytes for the quota scenario for this reason. Setting enabled: false does not disable quotas, which surprises operators.

Suggested fix

Measure usage under the storage root (a cached walk, as perUserBytes already does), or rename and document the setting as a filesystem-level floor. Honour enabled: false by not installing the decorator.


Found while building the cairn-desktop integration tests (phase 2, Cordy/cairn-desktop#29 scenario 9). Filed by Claude on behalf of @Cordy.

**Status: from source** (read at `bd006ef`, not reproduced). Line numbers at `bd006ef`. ## What happens - `maxTotalBytes` is checked as `usage.Used >= instanceTotal` (`internal/storage/quota/quota.go:113–119`). On POSIX, `Used` is the **whole filesystem's** statfs usage, `(Blocks − Bfree) × Bsize` (`internal/storage/posix/usage_unix.go:16–26`) — not what is stored under the storage root. - `storage.quota.enabled` only gates a log line; the quota decorator is always installed with whatever limits are set (`cmd/cairnd/main.go:578–595` at `bd006ef`). ## Impact On a volume shared with anything else (the OS disk, other services, a NAS), `maxTotalBytes` either blocks every write from the start or never triggers. cairn-desktop's integration tests had to use `perUserBytes` for the quota scenario for this reason. Setting `enabled: false` does not disable quotas, which surprises operators. ## Suggested fix Measure usage under the storage root (a cached walk, as `perUserBytes` already does), or rename and document the setting as a filesystem-level floor. Honour `enabled: false` by not installing the decorator. --- Found while building the cairn-desktop integration tests (phase 2, Cordy/cairn-desktop#29 scenario 9). Filed by Claude on behalf of @Cordy.
Cordy closed this issue 2026-09-12 16:34:59 +00:00
Author
Owner

Shipped in v0.6.170 (PR #505), live on both dogfoods (cairn_build_info{version="v0.6.170"} verified).

What landed:

  • maxTotalBytes now measures the bytes stored under the storage root — the same cached tree walk the per-user cap uses (~30s TTL, so enforcement can lag a burst of writes by up to that window; each accepted write also updates the cache incrementally). It no longer reads the filesystem's statfs, so on a shared volume the cap neither trips on other tenants' data nor stays blind when the disk is huge. Works on every backend — no Usager needed any more.
  • Admin storage meter follows suit: when an instance cap is set, Usage() reports Used as the under-root figure (Total = the cap), so the meter and the enforcement agree.
  • reserveBytes is unchanged by design — it stays a statfs free-space floor ("don't fill the disk"), which genuinely is about the whole filesystem.
  • storage.quota.enabled: false now really disables enforcement: the field is tri-state (*bool); explicit false keeps the quota decorator out of the storage chain entirely, and admin-set caps are inert until re-enabled. Leaving the setting out keeps the always-installed behaviour from #178.

TDD: TestInstanceCapMeasuresUnderRoot / TestInstanceCapIgnoresSharedDiskUsage / TestInstanceCapUsageReportsUnderRoot witnessed red on the runner against the old statfs check; TestMaxTotalBytes (statfs semantics) retired, the no-Usager guard test retargeted at reserveBytes only; new TestQuotaEnabledTriState covers absent-vs-false config parsing.

Shipped in **v0.6.170** (PR #505), live on both dogfoods (`cairn_build_info{version="v0.6.170"}` verified). What landed: - **`maxTotalBytes` now measures the bytes stored under the storage root** — the same cached tree walk the per-user cap uses (~30s TTL, so enforcement can lag a burst of writes by up to that window; each accepted write also updates the cache incrementally). It no longer reads the filesystem's statfs, so on a shared volume the cap neither trips on other tenants' data nor stays blind when the disk is huge. Works on every backend — no `Usager` needed any more. - **Admin storage meter follows suit**: when an instance cap is set, `Usage()` reports Used as the under-root figure (Total = the cap), so the meter and the enforcement agree. - **`reserveBytes` is unchanged by design** — it stays a statfs free-space floor ("don't fill the disk"), which genuinely is about the whole filesystem. - **`storage.quota.enabled: false` now really disables enforcement**: the field is tri-state (`*bool`); explicit `false` keeps the quota decorator out of the storage chain entirely, and admin-set caps are inert until re-enabled. Leaving the setting out keeps the always-installed behaviour from #178. TDD: `TestInstanceCapMeasuresUnderRoot` / `TestInstanceCapIgnoresSharedDiskUsage` / `TestInstanceCapUsageReportsUnderRoot` witnessed red on the runner against the old statfs check; `TestMaxTotalBytes` (statfs semantics) retired, the no-`Usager` guard test retargeted at `reserveBytes` only; new `TestQuotaEnabledTriState` covers absent-vs-false config parsing.
Sign in to join this conversation.
No labels
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: Cordy/Cairn#497
No description provided.