Skip to content

fix(sandbox): carry a full-pool capacity notice into sandbox.get (#649) - #655

Merged
mengmengmengqiang merged 1 commit into
layerfs_v2_0_0from
fix/649-down-capacity
Nov 12, 2026
Merged

mengmengmengqiang merged 1 commit into
layerfs_v2_0_0from
fix/649-down-capacity

Conversation

@mengmengmengqiang

Copy link
Copy Markdown
Contributor

Summary

sandbox.get omits daemon_capacity exactly when the daemon pool is full, because the capacity read needs the same slot. The only source of the counts then is the pre-handshake LAYERFS-CAPACITY notice, and direct::unanswered dropped its detail when it re-coded the refusal to Unavailable. This exposes the notice as a new explicit, partial field instead of faking daemon_capacity:

down?: { code, resource, pool_vacant, reserved_vacant, retained }
       alongside down_reason
  • control/crates/layerfs-sandbox/src/direct.rs::unanswered keeps the Capacity refusal's detail (it already carries resource, pool_vacant, reserved_vacant, retained, retryable, retry_after) and tags it with code: "Capacity". Every other refusal keeps no counts.
  • control/crates/layerfs-sandbox/src/get.rs builds the Down model only when all five fields are present. A legacy bare EOF (detail Null), a malformed record, or a remote refusal collapsed to {phase} by values::refusal() yields None, so down is absent rather than zeros.
  • api/crates/layerfs-api/src/sandbox/get.rs shows down next to down_reason. It is never mapped into daemon_capacity: CapacityModel::parse fills missing fields with 0, so a partial notice would read as pool_running: 0 / no failures.
  • Capacity still does not pass through unanswered, so sandbox.get keeps answering with down_reason instead of failing.

Test evidence

Dell aluminium (x86_64, 88 cores), rustc 1.85.1, branch on merged layerfs_v2_0_0 @ dbd633fac (#650/#651/#652):

cargo +1.85.1 fmt --all -- --check                                              # rc=0
cargo +1.85.1 clippy --workspace --exclude layerfs-node --all-targets -- -D warnings   # rc=0
cargo +1.85.1 test -p layerfs-sandbox -p layerfs-api --no-fail-fast             # rc=0
    164 passed, 0 failed
    tests/sandbox_capacity.rs: 8 passed, 0 failed, in 60.34 s
      a_full_daemon_pool_shows_the_partial_down_counts ... ok
      a_legacy_eof_has_no_down_counts ... ok
      a_running_sandbox_s_get_shows_its_daemon_capacity ... ok   (down absent, capacity present)
      a_collapsed_remote_refusal_has_no_down_counts ... ok

The full-pool test reproduces the measured behaviour: the get waits the whole 60 s admission window (asserted >= 55 s; the run took 60.34 s) and then answers with state: running, down_reason, and down = { code: "Capacity", resource: "daemon_control_slot", pool_vacant: 0, reserved_vacant: 1, retained: 3 }, with daemon_capacity absent.

Full log: /home/dell/649-final.log on aluminium. The suite runs as the non-root dell user (the daemon slot tests need it); cargo test -p layerfs-daemon --test daemon_slots --test slot_state is 27/27 on this base. Nothing that needs a real kernel mount is touched by this change.

Docs: docs/architecture/api/sdk/sandbox/get.md, sandbox/README.md, docs/architecture/control/routing/daemon-channel.md.

Closes #649.

`sandbox.get`'s `daemon_capacity` model is absent exactly when the daemon
pool is full: the capacity read needs the same slot, so the only source of
the counts is the pre-handshake `LAYERFS-CAPACITY` notice. `direct::unanswered`
re-coded every refusal that was not `Busy`/`StaleSandbox` to `Unavailable`
and dropped the notice's `detail`, so the get could only show prose in
`down_reason`.

Keep the `Capacity` detail (tagged with its code) through `unanswered`, and
surface it from `sandbox.get` as a new explicit, **partial** `down` field:

    down: { code, resource, pool_vacant, reserved_vacant, retained }

It is not mapped into `daemon_capacity` (`CapacityModel::parse` would fill
missing fields with zero). It is absent when the refusal had no counts: a
legacy bare EOF, a malformed record, or a remote refusal collapsed to
`{phase}` by `values::refusal`. `Capacity` still does not pass through
`unanswered` as `Capacity`, so the get keeps answering with `down_reason`.

Tests (layerfs-api/tests/sandbox_capacity.rs, 8/8):
- full pool -> get shows `down` with the notice counts, after the measured
  60 s admission window, and no `daemon_capacity`;
- legacy EOF -> `down_reason`, no `down`;
- normal get -> no `down`, `daemon_capacity` intact;
- collapsed remote refusal `{phase}` -> `Down::of` is `None`.

Docs: sandbox/get.md, sandbox/README.md, daemon-channel.md.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant