numinnex commented on PR #3753:
URL: https://github.com/apache/iggy/pull/3753#issuecomment-5093410562

   Thanks for this review -- the identity model was the right thing to block 
on, and two of your findings turned out to be load-bearing in ways I argued 
against before coming around. Everything from the numbered blocker list is 
addressed; details are in the per-thread replies, and I have resolved the 
threads that are fully closed. Summary of what changed and what I deliberately 
left open.
   
   ## The identity model
   
   **Fence is log-derived again.** `commit_register` sets the entry's epoch to 
the register's commit op rather than a per-entry counter. Your remedy #3, which 
I initially declined on the grounds that an op-derived value would not be 
comparable across planes -- that conflated the fence *value* with the table's 
*identity*. The key stays client-supplied; Register commits only in the 
metadata group, so there is one minting authority and any group's slice stores 
a plain `u64`. What the counter was quietly trading away is the thing you were 
pointing at: a counter *resets* when an entry is dropped by capacity eviction 
and re-created, so a zombie holding epoch 1 and a fresh incarnation holding 
epoch 1 are indistinguishable. Ops do not reset.
   
   **Binds commit.** The fast-path short-circuit in 
`submit_register_in_process` is gone and `register_preflight` no longer 
consults the table (`check_register` and `RequestStatus::AlreadyRegistered` 
deleted), so every bind proposes and the epoch actually moves. 
`Fenced`/`EpochAhead` are reachable on the live path for the first time -- the 
inertness you flagged. The ownership gate stays ahead of the dispatch and still 
refuses another user's entry; an owned entry falls through to a real Register 
whose rebind branch preserves watermark and reply ring. Cost is one consensus 
round trip per reconnect, which is the price of a bind being a logged event.
   
   **Credential-free resume is deleted, not gated.** `try_resume_session` and 
`MetadataSubmit::ResumeLookup` are gone; resume runs through the login path, 
which already did it correctly. That took findings #1, #2, #5, #6 and #9's 
first half with it.
   
   **`client_seq` no longer starts fresh into a recovered table.** The 
coordinator reseeds the minter above the highest recovered client-id sequence 
at boot, before any listener accepts. This makes the restart re-mint collision 
*unreachable* rather than merely refused, which demotes the ownership gate to a 
backstop.
   
   ## Apply-path robustness
   
   `commit_reply` returns a typed `CommitReply { Cached, NoEntry, 
SkippedRegression }` instead of asserting; callers log it. The wire reply ships 
either way, so a skip degrades one entry's dedup rather than taking down a 
shard pump or refusing to boot. The unreachable `epoch` param went with it. 
`install_client_table` is a logged refusal returning `bool`, not an `assert!` 
-- your point that a boot-path panic is worse than a refusal.
   
   `evict_oldest` no longer consults pipeline state, so the victim choice 
depends only on committed state. It also no longer casts headers: `client_id` 
and `latest_commit` are denormalized onto the entry, and it returns the freed 
slot so `commit_register` skips the second array walk.
   
   ## Correctness
   
   - PAT replay: a replayed `CreatePersonalAccessToken` is refused with 
`PersonalAccessTokenAlreadyExists` rather than served a cached success with a 
freshly minted secret spliced in. The complete fix is derivable secrets so a 
replay can reproduce the original token; that is a wire change and wants its 
own issue.
   - HTTP request id is burned at stamp time, before any fallible work.
   - `AlreadyApplied` now answers with a terminal `RequestAlreadyApplied` code 
instead of silence, so the client learns immediately instead of waiting out its 
read timeout. I pushed back on this initially; you were right that terminal 
deserves a frame.
   - The checksum reuse check runs at every cached-reply hit, not just at the 
watermark.
   - Slot reclaim: non-group-member disconnects defer reclaim by a grace window 
and a sweeper submits the `Logout`. Verifying that turned up a leak in my own 
fix -- the drained reclaim was dropped on a transient submit failure, leaving 
exactly the slot-until-eviction case your comment describes. The sweeper now 
re-arms before submitting and clears only on a committed `Logout`.
   - `Operation::Reserved` is rejected at ingress, so the `Reserved, request = 
0` path can no longer reach the dedup preflight ahead of the operation gate.
   - Duplicate match arms collapsed; `ClientEntry` is private; 
`session`/`request_checksum` documented on all three headers; stale contiguity 
and `SessionMismatch` docs swept.
   
   ## Tests
   
   Ingress validation is pinned (`RequestHeader::validate` through 
`try_into_typed`: the field rules table plus zero-client rejection), since 
several conclusions on both sides turned on whether it runs. The replay proof 
is now direct -- `commit_request` returns the committed reply and the retry is 
asserted byte-identical, with `op` equality being what rules out a re-apply; it 
no longer rests on `CreateStream` rejecting a duplicate name. Both restart 
specs run single-node and 3-node. New unit coverage for the reclaim retry, the 
minter reseed, the terminal refusal, and the denormalized eviction ranking.
   
   ## Open, by intent
   
   - **Watermark surfaced to SDKs.** The gateway case is closed (the register 
submit returns `BoundSession { epoch, watermark }` and HTTP seeds its numbering 
above the inherited watermark), so no wire change was needed. An SDK resuming 
across its *own* process restart still needs it in the bind reply; that is 
tier-2 identity and wants the protocol discussion.
   - **Sessions below the snapshot floor** (recovery.rs:201) -- needs the table 
in the checkpoint, which is the state-transfer work.
   - **Status is not re-checked for bound sessions.** Deactivating a user does 
not stop its live sessions. Master has the identical gap (same two 
`remove_client` sites, same login-only check, no status reference in the gate), 
so it is not from this PR; deletion *is* enforced per-op because `DeleteUser` 
purges the permissioner and the rules fail closed. Filing separately, which is 
why I left that thread open.
   - Replayed `Logout` group staleness, and `Reserved`'s post-preflight gate 
ordering beyond the ingress fix.
   
   I also corrected two of my own earlier replies in this thread where the 
epoch rework made them wrong.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to