Commit 0206a1c
committed
feat(sqs): receipt-handle v2 codec for partitioned FIFO queues (Phase 3.D PR 5a)
Adds the v2 receipt-handle layout that PR 5b's send / receive
fanout will consume. Pure scaffold: the v2 encoder is added but
not yet called by any production path (PartitionCount > 1 is
still rejected by the §11 PR 2 dormancy gate, so SendMessage
never reaches a partitioned queue, so encodeReceiptHandleV2 stays
unreachable until PR 5b lifts the gate).
Splitting the codec out as a small isolated PR keeps PR 5b's
review focused on the gate-and-lift atomic sequence — codec
correctness and the gate-lift correctness are independent
concerns and a single mega-PR would conflate them.
Wire format
v1 (legacy, single partition):
[ 0 ] byte version = 0x01
[ 1..9 ] uint64 queue_gen (BE)
[ 9..25 ] 16 bytes message_id
[ 25..41 ] 16 bytes receipt_token
Total: 41 bytes
v2 (partitioned, PartitionCount > 1):
[ 0 ] byte version = 0x02
[ 1..5 ] uint32 partition (BE) ← NEW
[ 5..13 ] uint64 queue_gen (BE)
[ 13..29 ] 16 bytes message_id
[ 29..45 ] 16 bytes receipt_token
Total: 45 bytes
decodedReceiptHandle gains Version (byte) and Partition (uint32)
fields. decodeReceiptHandle dispatches by the version byte; v1
keeps Partition=0 by definition.
What does NOT change yet
- encodeReceiptHandleV2 is dormant until PR 5b's SendMessage
partitioned-fanout dispatch wires it (gated by
meta.PartitionCount > 1). Production traffic continues to use
the v1 encoder verbatim — no behaviour change.
- DeleteMessage / ChangeMessageVisibility still consume
decodedReceiptHandle without inspecting Version or Partition.
PR 5b adds the cross-version rejection contract (a v1 handle
against a partitioned queue → ReceiptHandleIsInvalid, and
vice-versa).
Tests
- TestEncodeReceiptHandleV2_RoundTrip — every (partition,
queue_gen, message_id, token) tuple round-trips with
Version=v2 and the encoder's partition.
- TestEncodeReceiptHandleV1_StillReportsV1 — regression: v1
encoder still produces v1-decodable handles with Partition=0.
- TestDecodeReceiptHandle_VersionDispatch — v1 and v2 produce
distinct on-wire bytes (different version + 4-byte size delta);
decoder picks the right layout for each.
- TestDecodeReceiptHandle_RejectsLengthMismatch — 5 cases (v1
byte / v2 length, v2 byte / v1 length, v1 truncated, v2
truncated, empty) all fail with the same opaque error.
- TestDecodeReceiptHandle_RejectsUnknownVersion — 0x00, 0x03,
0x42, 0xFF all reject.
- TestEncodeReceiptHandleV2_RejectsBadInputs — short / long
token, non-hex / short / empty id all surface as encoder errors.
- TestReceiptHandleVersionConstants_Distinct — pins the
invariants v1 != v2, v1 == 0x01, v2 == 0x02, legacy alias
points at v1, on-wire size constants match what encoders write.
- TestDecodeReceiptHandle_RejectsBase64Garbage — base64 errors
surface at the right step.
- Existing TestSQSServer_ReceiptHandleCodecRoundTrip continues
to pass unchanged (v1 path).
Self-review (per CLAUDE.md)
1. Data loss — codec only; no FSM/Pebble path. No issue.
2. Concurrency — no shared state; pure encode/decode functions.
No issue.
3. Performance — encode/decode are O(handle size) ≤ 45 bytes.
The version-byte dispatch is one switch. No issue.
4. Data consistency — v1 and v2 are length-distinct, version-byte-
distinct, and decoder rejects wrong-length blobs. A v1 client
parsing a v2 handle (or vice versa) fails closed at the
length check — pinned by the 5-case rejection test. No issue.
5. Test coverage — 8 new tests across the codec contract surface;
existing v1 round-trip test continues to pass.1 parent e9f33eb commit 0206a1c
2 files changed
Lines changed: 374 additions & 14 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
46 | | - | |
47 | | - | |
48 | | - | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
49 | 67 | | |
50 | 68 | | |
51 | 69 | | |
| |||
213 | 231 | | |
214 | 232 | | |
215 | 233 | | |
216 | | - | |
217 | | - | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
218 | 247 | | |
219 | | - | |
| 248 | + | |
220 | 249 | | |
221 | 250 | | |
222 | 251 | | |
223 | 252 | | |
224 | | - | |
225 | | - | |
| 253 | + | |
| 254 | + | |
226 | 255 | | |
227 | 256 | | |
228 | 257 | | |
| |||
231 | 260 | | |
232 | 261 | | |
233 | 262 | | |
234 | | - | |
235 | | - | |
| 263 | + | |
| 264 | + | |
236 | 265 | | |
237 | 266 | | |
238 | 267 | | |
239 | 268 | | |
240 | 269 | | |
241 | 270 | | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
242 | 316 | | |
| 317 | + | |
| 318 | + | |
243 | 319 | | |
244 | 320 | | |
245 | 321 | | |
246 | 322 | | |
247 | 323 | | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
248 | 328 | | |
249 | 329 | | |
250 | 330 | | |
251 | 331 | | |
252 | 332 | | |
253 | | - | |
254 | | - | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
255 | 342 | | |
256 | 343 | | |
257 | | - | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
258 | 353 | | |
259 | 354 | | |
260 | 355 | | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
261 | 362 | | |
262 | | - | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
263 | 370 | | |
264 | 371 | | |
265 | 372 | | |
| |||
0 commit comments