Uh oh!
There was an error while loading. Please reload this page.
fix: reject cross-segment ExclusiveStartKey in parallel Scan - #203
fix: reject cross-segment ExclusiveStartKey in parallel Scan#203LeeroyHannigan wants to merge 4 commits into
Conversation
jcshepherd
left a comment
There was a problem hiding this comment.
It's a little bizarre that we're executing a SQL query to do a mathematical calculation. I get why but in a perfect world I wonder if we'd be taking a dependency on a Postgres internal hash function for assigning segments.
A parallel Scan (TotalSegments/Segment) accepted an ExclusiveStartKey that belongs to a different segment, silently returning a truncated or empty page. Validate the start key against the same segment-assignment function used for the scan and reject a mismatch with a ValidationException naming the correct Segment, so a key returned as a LastEvaluatedKey for one segment is only valid when the same segment is re-scanned. Verified against DynamoDB. Adds a scan integration test. Signed-off-by: Lee Hannigan <lhnng@amazon.com>
0064bb1 to
43200aaCompareA parallel `Scan` resumed with a `LastEvaluatedKey` produced by a different segment returned a silently wrong page instead of an error. The refusal existed only in the Postgres scan path, so MongoDB and SQLite fabricated a page from whichever rows their own segment predicate happened to match. The MongoDB integration test `parallel_scan_rejects_cross_segment_exclusive_start_key` proved this: it received three items where it expected a ValidationException. The check cannot live in shared engine code as pure logic, because each backend assigns segments with its own private function: Postgres (hashtext(pk)::bigint & 2147483647) % total = segment MongoDB crc32(pk_text) % total = segment SQLite rowid % total = segment, not a function of the key at all So the split follows the ADR-0005 pattern: the engine owns the rule and the measured service message, and the storage trait gains one primitive, `scan_key_in_segment`, that each backend answers for itself. Postgres keeps its existing `hashtext` query (moved out of `query_scan` behind the trait method), MongoDB answers with a pure CRC32 computation and no round trip, and SQLite resolves the key to its rowid. Two decisions worth recording: `Ok(true)` means "cannot be proven foreign", not "proven local". SQLite's assignment depends on storage identity rather than key content, so a key it cannot resolve must be admitted: DynamoDB permits an `ExclusiveStartKey` for an item that no longer exists, and refusing it would reject a legitimate resumption after a delete. The trait method is required rather than defaulted. A default returning `true` would silently disable the refusal for any future backend, which is the exact failure this commit fixes. Verification, per backend, live against a running server: sqlite 13/13 scan integration tests postgres 13/13 scan integration tests mongodb 13/13 scan integration tests Each backend also has a negative control: sabotaging its `scan_key_in_segment` to return `Ok(true)` unconditionally makes the discriminating test fail with the fabricated page, confirming the test distinguishes the fix from its absence rather than passing incidentally. fmt clean; clippy `--all-targets -D warnings` clean on the sqlite and postgres feature sets; 943 lib tests passed, 0 failed, 0 filtered on both. The mongodb clippy failure (`install_backend` dead code in `crates/bin/src/main.rs`) is pre-existing on this branch's base and reproduces identically with these changes stashed.
LeeroyHannigan
commented
Aug 19, 2026
Pushed The failing test was correct. It can't be shared logic in the engine, because each backend assigns segments differently and privately:
So it follows the ADR-0005 split: the engine owns the rule and the measured service message, and the storage trait gains one primitive, @jcshepherd this also speaks to your point about the SQL query doing a mathematical calculation. It's still a query on Postgres, but the dependency on Two decisions worth flagging for review:
The trait method is required, not defaulted. A default returning Verification, live against a running server for each backend:
Each backend also has a negative control: sabotaging its fmt clean; clippy |
What
Reject a cross-segment
ExclusiveStartKeyin a parallelScan.A parallel
Scan(TotalSegments/Segment) accepted anExclusiveStartKeybelonging to a different segment, silently returning a truncated or empty page. This validates the start key against the same segment-assignment function usedfor the scan and rejects a mismatch with a
ValidationExceptionnaming the correctSegment, so a key returned as aLastEvaluatedKeyfor one segment is only valid when the same segment is re-scanned.Why
DynamoDB validates that an
ExclusiveStartKeysupplied to a parallel scan belongs to the requested segment, and rejects it otherwise; the engine instead accepted any key and combined it with the segment predicate, silently yielding a truncated or empty page rather than an error. This is a correctness/parity gap for parallel-scan pagination. The behavior was verified against the real DynamoDB service.Closes #
Testing done
ExclusiveStartKey, and rejected with aValidationExceptionwhen supplied for a different segment.ExclusiveStartKeypaginates within the segment; a cross-segment key returnsValidationException: The provided starting key is invalid: Invalid ExclusiveStartKey. Please use ExclusiveStartKey with correct Segment. TotalSegments: 4 Segment: N.cargo test -p extenddb-storage-postgresunit pass; scan integration test passes;cargo clippy --workspace --all-targetsandcargo fmt --all -- --checkclean.Checklist
cargo test --workspace)cargo fmt --check)cargo clippy -- -W clippy::pedantic)Storagetrait, auth model, on-diskformat, or public CLI surface, an RFC has been accepted or is linked
below. Otherwise, an ADR captures the decision (link below).
ADR / RFC: n/a -- no change to wire protocol,
Storagetrait, auth model,on-disk format, or public CLI surface. Request-validation correctness only.
Breaking changes
None. A request that was previously mis-accepted (a cross-segment
ExclusiveStartKey) now returns the DynamoDB-correctValidationException; valid same-segment pagination is unchanged.By submitting this pull request, I confirm that my contribution is made under
the terms of the Apache License 2.0 and I agree to the Developer Certificate of
Origin (DCO). See CONTRIBUTING.md for details.