fix: MultiRead zxid and watch registration - #397
Conversation
Two fixes for MultiRead (OpNum 22) processing:
1. zxid: MultiRead no longer consumes a zxid. Added OpNum::MultiRead to
shouldIncreaseZxid() exclusion list. MultiRead is a read-only operation —
only write requests should advance the global transaction ID.
2. Unbundle & watches: MultiRead subrequests are now processed individually
instead of through StoreRequestMultiTxn. Each subrequest gets:
- Independent ACL check
- Per-subrequest watch registration (was silently dropped before —
only the outer MultiRead wrapper's has_watch was checked, which is
always false)
- Individual error codes preserved per subrequest
Added an allowlist guard rejecting write ops (Create/Set/Remove) inside
MultiRead — without this they would bypass Raft consensus.
Tests: 3 unit (zxid stability, watch registration, per-subrequest errors)
+ 2 integration (zxid via mntr, watches via wchc 4LW)
Co-Authored-By: Claude <noreply@anthropic.com>
|
Unit test report for commit d8aa1fc. All test cases passed! Successful Test Cases
|
|
Integration test report for commit d8aa1fc. All test cases passed! Successful Test Cases
|
- test_multi_read_zxid_stability: switch from mntr to srvr 4LW command. mntr uses zk_key\tvalue format and doesn't include Zxid; srvr uses Key: value format including Zxid. - test_multi_read_subrequest_watch: add missing exists() method to MultiReadRequest and import Exists from kazoo.client. Co-Authored-By: Claude <noreply@anthropic.com>
MultiRead.deserialize() returns get_children as a bare list, not a tuple. results[2] is already ['a', 'b'] — indexing with [0] gave 'a' (a single char). Co-Authored-By: Claude <noreply@anthropic.com>
Code reviewFound 3 test coverage gaps:
RaftKeeper/src/Service/KeeperStore.cpp Lines 1363 to 1375 in 78f5725
RaftKeeper/src/Service/KeeperStore.cpp Lines 1379 to 1383 in 78f5725
RaftKeeper/src/Service/KeeperStore.cpp Lines 1391 to 1394 in 78f5725 Coverage summary:
🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
…NONODE watch Covers three review findings: - MultiReadRejectsWriteOps: verifies Exception is thrown when Create/Set/Remove subrequests appear inside a MultiRead - MultiReadExistsWatchOnNonExistentNode: verifies Exists with watch=true on a nonexistent path still registers the watch (exercises the ZNONODE + Exists conditional branch) - MultiReadAuthCheckPerSubrequest: verifies per-subrequest ACL checks — a public node returns ZOK, a digest-only node returns ZNOAUTH Co-Authored-By: Claude <noreply@anthropic.com>
Code review (follow-up)All 3 previously flagged coverage gaps are now addressed:
Updated coverage:
No further issues found. LGTM. 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
Which issues of this PR fixes:
This PR try to fix #
Change log:
Two fixes for MultiRead (OpNum 22) processing:
zxid: MultiRead no longer consumes a zxid. Added OpNum::MultiRead to shouldIncreaseZxid() exclusion list. MultiRead is a read-only operation — only write requests should advance the global transaction ID.
Unbundle & watches: MultiRead subrequests are now processed individually instead of through StoreRequestMultiTxn. Each subrequest gets:
Added an allowlist guard rejecting write ops (Create/Set/Remove) inside
MultiRead — without this they would bypass Raft consensus.
Tests: 3 unit (zxid stability, watch registration, per-subrequest errors)
+ 2 integration (zxid via mntr, watches via wchc 4LW)