Commit fe96c88
authored
adapter/sqs: AdminPeekQueue backend (Phase 3) (#794)
## Summary
Phase 3 of `docs/design/2026_05_16_proposed_admin_purge_queue.md`:
backend `AdminPeekQueue` method on `*adapter.SQSServer`.
- Returns up to `opts.Limit` currently-visible messages (`visible_at <=
now`); pure read, no receive-count bump, no receipt-handle minting, no
visibility-timer change.
- Partitioned FIFO support via rotated sequential scanning across
partitions, reusing the receive path's `receiveFanoutCounters` for
fairness.
- Versioned JSON + base64url cursor pins `Generation`, `StartPartition`,
`Partition`, and `LastKey`; stale-generation cursors are rejected so a
purge between pages forces the SPA to refresh from the front.
- `AdminRole.canRead()` — peek requires non-zero role; read-only
principals can triage, purge stays on `canWrite()`.
- Pre-existing `goconst` regression in `sqs_messages.go` (the validator
switch's inline `"String"` / `"Number"` literals) fixed by extracting
`sqsAttributeBaseTypeString` / `sqsAttributeBaseTypeNumber` alongside
the existing `sqsAttributeBaseTypeBinary`.
**Caller audit (semantic-change rule):** no existing function signatures
changed. `canRead` is a new method; `AdminPeekQueue` is a new
entrypoint. The validator switch's constant extraction is a
literal-for-named rename (byte-identical wire behaviour).
## Behavior change
- New `AdminPeekQueue` admin method. Not yet wired to an HTTP handler
(Phase 4) — this PR only adds the adapter surface.
- No change to SigV4 paths, `AdminPurgeQueue`, or `AdminDescribeQueue`.
## Risk
Low. Pure-read entry; the only writes I touch are the constant rename in
`sqs_messages.go` (byte-identical to the prior inline literals). The
cursor codec is hard-capped at 512 bytes after encoding; oversize
cursors surface as `ErrAdminSQSValidation` from the decoder.
## Self-review (5 passes)
1. **Data loss** — Read-only entry. The constant extraction in
`sqs_messages.go` is a literal-for-named rename with no behaviour
change.
2. **Concurrency** — Leader-only via `isVerifiedSQSLeader`. Single
`nextTxnReadTS` threaded into `loadQueueMetaAt`, `ScanAt`, and per-row
`GetAt`, so the page is a consistent MVCC snapshot. The cursor's stored
Generation is checked against the live meta on every continuation page —
a purge between pages produces `ErrAdminSQSValidation` rather than rows
from a purged generation.
3. **Performance** — `O(opts.Limit)` `ScanAt` round-trips + per-row
`GetAt`. Hard caps: `Limit <= 100`, body <= 256 KiB. Cursor is bounded
at 512 bytes. No hot-path allocations.
4. **Consistency** — Cursor encodes `Generation`; mismatch surfaces as
`ErrAdminSQSValidation`. Partition rotation uses the same
`receiveFanoutCounters` the receive path uses, so peek's partition
fairness matches the data plane's. Data records are read via
`sqsMsgDataKeyDispatch`, so partitioned FIFO data keys are routed to the
same partition the vis-index entry was found under.
5. **Test coverage** — 22 new tests (happy path / no-bump-on-peek /
truncation / Limit clamp / cursor round-trip / stale-gen / four
malformed-cursor classes / read-only allowed / role-less denied /
missing queue / empty name / delayed-message hidden / FIFO attribute
projection / typed MessageAttribute (String+Binary) round-trip /
partitioned FIFO pagination / cursor codec round-trip+empty+oversize /
preparePeekCursor fresh+stale / clampPeekLimit+clampPeekBodyBytes truth
tables / projectPeekedAttributes nil-safety / canRead truth table). `go
test -race` clean.
## Test plan
- [x] `go test -race -count=1 ./adapter/...` (targeted:
`TestAdminPeekQueue*`, `TestPeekCursorCodec*`, `TestClampPeek*`,
`TestPreparePeekCursor*`, `TestEncodePeekCursor*`,
`TestProjectPeekedAttributes*`, `TestAdminRole_CanRead`,
`TestAdminPurge*`, `TestAdminQueueSummary*`,
`TestSQSServer_PartitionedFIFO*`, `TestSQSServer_Send*`)
- [x] `make lint` clean on `./adapter/...` (0 issues)
- [ ] CI on this PR
## Out of scope (follow-ups)
- **Throttle integration**: dedicated per-queue admin-peek bucket
(`bucketActionAdminPeek` + `resolveActionConfig` explicit case +
`*adminPeekThrottledError` + `*PeekThrottledError`). Leader-only +
`Limit <= 100` already bound the steady-state cost; the dedicated bucket
adds a per-queue cap that should land alongside the SPA wiring so the
metric has a real consumer.
- **Phase 4**: HTTP handler at `GET
/admin/api/v1/sqs/queues/{name}/messages`, bridge in `main_admin.go`,
integration tests.
- **`principalForReadSensitive` live `RoleStore` re-check** (design doc
Goal 8): blocked on the wider `RoleStore` plumbing — neither
`AdminPurgeQueue` nor `AdminDeleteQueue` do a live re-check today; peek
will inherit that pattern when the broader work lands.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Admin API to peek SQS queue messages without modifying them:
pagination with continuation tokens, configurable body truncation while
reporting original size, full message attribute visibility, supports
standard and FIFO queues.
* **Bug Fix / Permissions**
* Read-only admin roles are now allowed to perform non-destructive admin
reads (peek).
* **Polish**
* Improved message-attribute typing/handling for more reliable attribute
projection.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/bootjp/elastickv/pull/794?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->4 files changed
Lines changed: 1714 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
86 | 96 | | |
87 | 97 | | |
88 | 98 | | |
| |||
0 commit comments