Skip to content

feat(dlv): the client speaks the generic endpoints, and restart resolves every fenced parent - #775

Open
cryptskii wants to merge 1 commit into
mainfrom
feat/quorumbind-http-transport-and-restart
Open

feat(dlv): the client speaks the generic endpoints, and restart resolves every fenced parent#775
cryptskii wants to merge 1 commit into
mainfrom
feat/quorumbind-http-transport-and-restart

Conversation

@cryptskii

Copy link
Copy Markdown
Collaborator

PR 4 of the QuorumBind restoration. The concrete HTTP BindingTransport (the SDK adapter, Req 15.8) that makes the PR 2 engine and PR 3 fence drive real storage members, plus the restart-recovery driver (Req 16.5). Builds on #774.

The adapter

quorum_bind_runner drives the sans-IO engine over a BindingTransport; HttpBindingTransport is its production implementation over the two generic endpoints Class N exposes:

  • POST /api/v2/storage/binding/readReadBinding, public.
  • POST /api/v2/storage/binding/casCompareExchangeMany, device auth.

The wire messages are the single canonical protobuf definitions (dsm::types::proto) the node itself decodes, so the two sides cannot drift. The node's own endpoint tests prove it honors those messages; this PR proves the client builds and parses the same ones.

Attribution (Req 15.8). Every answer carries the member's member_id and register_incarnation; the transport reports both, and the runner (PR 2) authenticates them against the committed member before counting. The transport itself adds no trust — it only relays what the member said. The node stays application-blind: the transport sends opaque keys, an expected digest, and a canonical record, never a claim, vault, or route.

A read maps cells back to keys. parse_read_response indexes the returned cells by key and rebuilds the record list in the requested order, and treats a response that omits or duplicates a requested key as unavailable, not a partial answer. A member that reorders or drops cells cannot shift a record onto the wrong key or forge an incomplete read (Req 15.13). A non-200 (503, or a transport failure) is unavailable; INVALID_STORAGE_ENCODING and EXPECTATION_MISMATCH come back in the 200 body and map through faithfully, while UNAVAILABLE never counts.

The restart driver

recover_unresolved_fences is the canonical restart entry (Req 16.5): it loads every locally frozen transaction whose trader-parent fence is not terminal (list_unresolved_fences) and hands each to a resume_one that reconstructs the transaction from the fence's stored storage set and immutable bundle address and drives it with run_fenced. A fence whose bundle is not yet retrievable is left fenced and surfaced as unresolved, so the parent cannot advance until a later pass resolves it. One worker per fence, in insertion order.

The reconstruction itself — resolving the set from the catalog, retrieving the bundle bytes via GetImmutable, and parsing K(B) and the trader successor out of the settlement bundle — is settlement-specific and belongs to the settle path, so resume_one is supplied there (PR 5). This PR delivers the driver, the transport it runs over, and the fence bookkeeping it enforces.

Tests

  • Codec (binding_http_transport, 4): a CAS request round-trips through the node message and a non-canonical replacement is refused; a read answer maps records back to their keys regardless of response order and carries attribution; a read missing a requested key is unavailable, not a partial answer; CAS outcomes map and UNAVAILABLE / non-200 do not count.
  • Restart (quorum_bind_runner, +1): two unresolved fences on different parents — the one whose bundle is retrievable resolves to a terminal outcome, the other is left fenced and its parent still blocks all successors.

Verification (Rust 1.98.0)

  • root make lintexit 0
  • workspace board (--workspace --exclude dsm_storage_node --release) — 4020 passed, 0 failed (75 suites)
  • production_safety_checks (all-features clippy + TLA+) — exit 0 (TLA+ included)

Not in this PR (by design)

  • The resume_one reconstruction (catalog resolve + GetImmutable + settlement-bundle parse to K(B)/successor) and wiring the transport into the live settle paths — PR 5.
  • Switching the three settle paths to QuorumBind and deleting the settlement-slot register — PR 5.
  • The transport's reqwest send is thin glue; its correctness rests on the codec (unit-tested here) and the single shared proto the node's endpoint tests exercise.

…ves every fenced parent

Rev 15 §15.5 / Req 15.8 / Req 16.5: the concrete HTTP BindingTransport that
makes the sans-IO engine (PR 2) and its fence (PR 3) drive real storage members,
plus the restart-recovery driver.

binding_http_transport: HttpBindingTransport over POST /api/v2/storage/binding/
{read,cas}, with a pure codec over the SINGLE canonical proto (dsm::types::proto)
the node itself decodes, so the two sides cannot drift. Every answer carries the
member's member_id and register_incarnation; the transport reports both and the
runner authenticates them against the committed member before counting (Req 15.8).
A read maps returned cells BACK to requested keys and treats a reordered,
duplicated, or incomplete set as unavailable, so a member cannot shift a record
onto the wrong key or forge a partial read (Req 15.13). A 503 or transport
failure is unavailable; INVALID_STORAGE_ENCODING and EXPECTATION_MISMATCH come
back in the 200 body and map through; UNAVAILABLE never counts.

recover_unresolved_fences: the canonical restart entry (Req 16.5). It lists every
non-terminal trader-parent fence and hands each to a resume_one that reconstructs
the transaction from the fence's stored set and immutable bundle address and
drives it with run_fenced. A fence whose bundle is not yet retrievable is left
fenced and surfaced unresolved, so the parent cannot advance until a later pass.
One worker per fence.

The reconstruction (catalog resolve + GetImmutable + settlement-bundle parse to
K(B) and the trader successor) is settlement-specific, so resume_one is supplied
by the settle path in PR 5.

Rust 1.98.0: workspace board 4020/0, make lint 0, production_safety_checks 0.
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