Skip to content

Commit df11d2b

Browse files
committed
feat(sqs): atomic dormancy gate-lift + cluster-wide htfifo capability gate (Phase 3.D PR 5b-3)
The §11 PR 2 dormancy gate (PartitionCount > 1 hard-rejected at CreateQueue) was a placeholder while the data plane caught up. The data plane is now in place (PR #731 + #732), so this PR atomically lifts the dormancy gate and replaces it with the §8.5 capability gate that polls every cluster peer's /sqs_health for the htfifo capability. What changes: - Remove validatePartitionDormancyGate and the htfifoTemporaryGateMessage constant from sqs_partitioning.go. Both were marked "Removed in PR 5 in the same commit that wires the data plane so the gate-and-lift land atomically" — that PR is this one. - Add (*SQSServer).validateHTFIFOCapability in adapter/sqs_capability_gate.go, called from createQueueCore. Two-stage fail-closed check on PartitionCount > 1: 1. Local: this binary must advertise htfifo (htfifoCapabilityAdvertised). Refuses the create with InvalidAttributeValue if not. 2. Peers: every entry in s.leaderSQS must report htfifo via /sqs_health within the poller's per-peer timeout. Any timeout, HTTP error, malformed body, or missing capability blocks the create. Vacuous on PartitionCount <= 1 and on empty leaderSQS (single- node cluster — the local check is the whole cluster). - collectSQSPeers helper returns leaderSQS values in deterministic sorted order with empty/duplicate addresses filtered, so the poller and operator-facing error messages never depend on Go map iteration order. - buildHTFIFOCapabilityRejection composes the rejection message with each failing peer's address + reason (per-peer Error or "missing capability") so an operator triaging a partial-rolling- upgrade cluster does not need to re-run the poll out-of-band. CreateQueue control flow on PartitionCount > 1: schema validators (validatePartitionConfig, etc.) → validateHTFIFOCapability → htfifoCapabilityAdvertised check (local) → PollSQSHTFIFOCapability(ctx, collectSQSPeers(), …) → reject with InvalidAttributeValue on any failure → createQueueWithRetry Caller audit: validateHTFIFOCapability has exactly one production caller (createQueueCore in sqs_catalog.go); both the JSON handler and the future query-protocol handler reach it through that one path. SetQueueAttributes is unaffected because PartitionCount is immutable post-create (validatePartitionImmutability). Test changes: - Delete TestValidatePartitionDormancyGate_RejectsAboveOne (the function it tested is gone). - Convert TestSQSServer_HTFIFO_DormancyGate_RejectsPartitionedCreate into TestSQSServer_HTFIFO_CapabilityGate_AcceptsOnSingleNode — the same wire payloads now SUCCEED because the local node advertises htfifo and there are no peers to poll. Renamed TestSQSServer_HTFIFO_DormancyGate_AllowsPartitionCountOne → TestSQSServer_HTFIFO_CapabilityGate_AllowsPartitionCountOne for consistency. - Update comments on TestSQSServer_HTFIFO_RejectsQueueScopedDedupOnPartitioned, TestSQSServer_HTFIFO_RejectsNonPowerOfTwoPartitionCount, TestSQSServer_HTFIFO_ImmutabilitySetQueueAttributesRejects, mustCreateFIFOWithThroughputLimit, and the installPartitionedMetaForTest helper to describe the new capability-gate world. New unit tests in sqs_capability_gate_test.go: - TestValidateHTFIFOCapability_ShortCircuitsOnLegacyMeta: PartitionCount in {0, 1} skips the poll entirely (proven by wiring a peer that would FAIL the gate and verifying the short-circuit path bypasses it). - TestValidateHTFIFOCapability_AcceptsWhenAllPeersAdvertise: happy path with two fake peers. - TestValidateHTFIFOCapability_AcceptsOnEmptyPeerList: vacuous case (single-node cluster). - TestValidateHTFIFOCapability_RejectsWhenOnePeerLacksCapability: rolling-upgrade fail-closed; offending peer's address surfaces in the InvalidAttributeValue message. - TestValidateHTFIFOCapability_RejectsWhenPeerUnreachable: transient-network fail-closed. - TestCollectSQSPeers_Deterministic: sort + dedup + empty-skip. - TestBuildHTFIFOCapabilityRejection_ShapesOperatorMessage: rejection-message shape pinned (advertising peers absent, failing peers contribute "(reason)" suffix, defensive paths). Self-review (CLAUDE.md): 1. Data loss — None. The gate strictly tightens CreateQueue acceptance vs. the previous dormancy reject; no path now accepts a write that would have been rejected before. The dormancy gate's invariant ("partitioned-shape meta cannot land on a binary that does not handle the partitioned keyspace") is preserved by the local htfifoCapabilityAdvertised check and strengthened by the cluster-wide poll. 2. Concurrency / distributed failures — Poll runs concurrently across peers via the existing PollSQSHTFIFOCapability helper (covered by its own race tests). collectSQSPeers + sort are pure / deterministic. The leaderSQS map is only mutated at SQSServer construction (WithSQSLeaderMap), not at request time, so no read/write races. Leader transitions during the poll are handled by the existing proxyToLeader path that gates createQueue before validateHTFIFOCapability runs. 3. Performance — Poll cost is O(peers) and only paid on PartitionCount > 1 creates (rare control-plane operation). Legacy / single-partition CreateQueue calls pay one short-circuit branch. collectSQSPeers' sort is O(N log N) on a small N (cluster size). No hot-path impact. 4. Data consistency — Schema validators (PartitionCount shape, dedup-scope rule, perMessageGroupId rule) still run BEFORE the capability gate inside parseAttributesIntoMeta, so an invalid shape rejects with the schema's reason rather than the gate's. SetQueueAttributes immutability remains the guard for post-create partition-shape changes. 5. Test coverage — Gate function: 5 unit tests covering the short-circuit, happy path, vacuous empty, rolling-upgrade, and unreachable-peer classes. Helpers: 2 unit tests pinning deterministic order and message shape. Wire-level: existing HT-FIFO integration tests carry forward, with the dormancy- reject test converted to the new accepts-on-single-node happy path.
1 parent 637e543 commit df11d2b

7 files changed

Lines changed: 387 additions & 101 deletions

adapter/sqs_capability_gate.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package adapter
2+
3+
import (
4+
"context"
5+
"net/http"
6+
"sort"
7+
"strings"
8+
)
9+
10+
// validateHTFIFOCapability is the §11 PR 5b-3 gate that replaced the
11+
// PR 2 dormancy reject. CreateQueue calls this on every request; it
12+
// is a no-op for legacy / single-partition meta and the full
13+
// cluster-wide capability check for partitioned FIFO meta.
14+
//
15+
// Two-stage check, both fail-closed:
16+
//
17+
// 1. Local: this binary must advertise the htfifo capability
18+
// (htfifoCapabilityAdvertised). If false, no amount of peer
19+
// polling can make the create safe — the leader handling the
20+
// request will write the partitioned-shape meta but its own
21+
// data plane does not understand the partitioned keyspace.
22+
//
23+
// 2. Peers: every entry in s.leaderSQS must report htfifo via
24+
// /sqs_health within the poller's per-peer timeout. Any
25+
// timeout, HTTP error, malformed body, or missing capability
26+
// blocks the create. This catches mid-rolling-upgrade clusters
27+
// where the leader is on a new binary but a follower is still
28+
// on the old one — the follower would silently store a
29+
// partitioned record under the legacy keyspace if it ever won
30+
// leadership, so we refuse the create until everyone is on a
31+
// binary that handles the new layout.
32+
//
33+
// The vacuous case (single-node cluster, leaderSQS empty) is
34+
// allowed: the local check covers the only node that will ever
35+
// host the queue. proxyToLeader has already steered the request to
36+
// the leader, and the leadership-refusal hook (PR 4-B-3b) keeps
37+
// non-htfifo binaries from acquiring leadership over partitioned-
38+
// queue Raft groups, so the gate's fail-closed default holds even
39+
// after the create succeeds.
40+
func (s *SQSServer) validateHTFIFOCapability(ctx context.Context, requested *sqsQueueMeta) error {
41+
if requested == nil || requested.PartitionCount <= 1 {
42+
return nil
43+
}
44+
if !htfifoCapabilityAdvertised {
45+
return newSQSAPIError(http.StatusBadRequest, sqsErrInvalidAttributeValue,
46+
"PartitionCount > 1 requires the htfifo capability, which this node does not advertise")
47+
}
48+
peers := s.collectSQSPeers()
49+
if len(peers) == 0 {
50+
// Single-node deployment: the local check above is the
51+
// whole cluster. Vacuously true on the peer side.
52+
return nil
53+
}
54+
report := PollSQSHTFIFOCapability(ctx, peers, PollerConfig{})
55+
if report == nil || !report.AllAdvertise {
56+
return newSQSAPIError(http.StatusBadRequest, sqsErrInvalidAttributeValue,
57+
buildHTFIFOCapabilityRejection(report))
58+
}
59+
return nil
60+
}
61+
62+
// collectSQSPeers returns every distinct, non-empty SQS-side address
63+
// from s.leaderSQS in deterministic (sorted) order. Used by the
64+
// CreateQueue capability gate so error messages and tests pin a
65+
// stable peer order. The map may legitimately contain self (the
66+
// proxy-to-leader path uses the same map to find the leader's SQS
67+
// address by Raft addr); polling self over loopback is cheap and
68+
// keeps the "every peer reports htfifo" invariant uniform.
69+
func (s *SQSServer) collectSQSPeers() []string {
70+
if len(s.leaderSQS) == 0 {
71+
return nil
72+
}
73+
peers := make([]string, 0, len(s.leaderSQS))
74+
seen := make(map[string]struct{}, len(s.leaderSQS))
75+
for _, addr := range s.leaderSQS {
76+
if addr == "" {
77+
continue
78+
}
79+
if _, ok := seen[addr]; ok {
80+
continue
81+
}
82+
seen[addr] = struct{}{}
83+
peers = append(peers, addr)
84+
}
85+
sort.Strings(peers)
86+
return peers
87+
}
88+
89+
// buildHTFIFOCapabilityRejection composes the operator-facing message
90+
// for a failed capability poll. Lists the peers that did not
91+
// advertise htfifo (with the per-peer Error or "missing capability"
92+
// reason) so the operator can fix the rolling-upgrade lag without
93+
// rerunning the poll out-of-band. Order matches report.Peers, which
94+
// matches collectSQSPeers' sorted input order — deterministic.
95+
func buildHTFIFOCapabilityRejection(report *HTFIFOCapabilityReport) string {
96+
var b strings.Builder
97+
b.WriteString("PartitionCount > 1 requires every cluster peer to advertise the htfifo capability via /sqs_health; the following peers did not: ")
98+
if report == nil {
99+
b.WriteString("(no report)")
100+
return b.String()
101+
}
102+
first := true
103+
for _, p := range report.Peers {
104+
if p.HasHTFIFO {
105+
continue
106+
}
107+
if !first {
108+
b.WriteString(", ")
109+
}
110+
first = false
111+
b.WriteString(p.Address)
112+
b.WriteString(" (")
113+
if p.Error != "" {
114+
b.WriteString(p.Error)
115+
} else {
116+
b.WriteString("missing capability")
117+
}
118+
b.WriteString(")")
119+
}
120+
if first {
121+
// Defensive: AllAdvertise was false but no peer surfaced a
122+
// reason. Should never happen, but emit a non-empty hint
123+
// rather than a truncated message ending in a colon.
124+
b.WriteString("(unknown peer)")
125+
}
126+
return b.String()
127+
}
Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
package adapter
2+
3+
import (
4+
"context"
5+
"errors"
6+
"net/http"
7+
"net/http/httptest"
8+
"strings"
9+
"testing"
10+
11+
"github.com/stretchr/testify/require"
12+
)
13+
14+
// htfifoCapabilityServer spins up a minimal /sqs_health responder
15+
// returning HTTP 200 with the given capabilities array. Stand-in
16+
// for a peer node during the capability-gate tests.
17+
func htfifoCapabilityServer(t *testing.T, capabilities []string) *httptest.Server {
18+
t.Helper()
19+
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
20+
if r.URL.Path != sqsHealthPath {
21+
http.NotFound(w, r)
22+
return
23+
}
24+
writeSQSHealthJSONBody(w, r, http.StatusOK, sqsHealthBody{
25+
Status: "ok",
26+
Capabilities: capabilities,
27+
})
28+
}))
29+
t.Cleanup(srv.Close)
30+
return srv
31+
}
32+
33+
// TestValidateHTFIFOCapability_ShortCircuitsOnLegacyMeta pins the
34+
// no-op path: PartitionCount <= 1 must skip the capability poll
35+
// entirely so legacy and single-partition CreateQueue calls do not
36+
// pay the cluster-wide poll cost.
37+
func TestValidateHTFIFOCapability_ShortCircuitsOnLegacyMeta(t *testing.T) {
38+
t.Parallel()
39+
40+
// Wire a peer whose /sqs_health would FAIL the gate, then
41+
// verify validateHTFIFOCapability does NOT poll it (the call
42+
// would otherwise reject). leaderSQS is non-empty so the
43+
// "vacuous empty list" path isn't what's saving us.
44+
bad := htfifoCapabilityServer(t, nil)
45+
s := &SQSServer{leaderSQS: map[string]string{"raft1": strings.TrimPrefix(bad.URL, "http://")}}
46+
47+
for _, pc := range []uint32{0, 1} {
48+
err := s.validateHTFIFOCapability(context.Background(), &sqsQueueMeta{PartitionCount: pc})
49+
require.NoErrorf(t, err, "PartitionCount=%d must skip the poll entirely (gate is HT-FIFO-only)", pc)
50+
}
51+
52+
// Defensive: nil meta also short-circuits — never reach the
53+
// poll for an unset/zero meta. Pinned so a future refactor
54+
// that added a poll on the nil path would fail loudly here.
55+
require.NoError(t, s.validateHTFIFOCapability(context.Background(), nil))
56+
}
57+
58+
// TestValidateHTFIFOCapability_AcceptsWhenAllPeersAdvertise pins
59+
// the happy path: every peer in leaderSQS reports the htfifo
60+
// capability via /sqs_health → the gate passes for
61+
// PartitionCount > 1.
62+
func TestValidateHTFIFOCapability_AcceptsWhenAllPeersAdvertise(t *testing.T) {
63+
t.Parallel()
64+
65+
caps := []string{sqsCapabilityHTFIFO}
66+
good1 := htfifoCapabilityServer(t, caps)
67+
good2 := htfifoCapabilityServer(t, caps)
68+
s := &SQSServer{leaderSQS: map[string]string{
69+
"raft1": strings.TrimPrefix(good1.URL, "http://"),
70+
"raft2": strings.TrimPrefix(good2.URL, "http://"),
71+
}}
72+
73+
require.NoError(t, s.validateHTFIFOCapability(context.Background(), &sqsQueueMeta{PartitionCount: 4}))
74+
}
75+
76+
// TestValidateHTFIFOCapability_AcceptsOnEmptyPeerList pins the
77+
// vacuous case: a single-node cluster (no peers) with the local
78+
// htfifo capability advertised must allow PartitionCount > 1.
79+
// htfifoCapabilityAdvertised is a build-time const = true so the
80+
// local check passes; the empty peer list short-circuits the
81+
// poll. This is the path the wire-level
82+
// TestSQSServer_HTFIFO_CapabilityGate_AcceptsOnSingleNode test
83+
// exercises end-to-end.
84+
func TestValidateHTFIFOCapability_AcceptsOnEmptyPeerList(t *testing.T) {
85+
t.Parallel()
86+
s := &SQSServer{}
87+
require.NoError(t, s.validateHTFIFOCapability(context.Background(), &sqsQueueMeta{PartitionCount: 4}))
88+
}
89+
90+
// TestValidateHTFIFOCapability_RejectsWhenOnePeerLacksCapability
91+
// pins the rolling-upgrade fail-closed: one peer advertises
92+
// htfifo, the other doesn't — the gate must reject the create
93+
// with InvalidAttributeValue and surface the offending peer in
94+
// the error message so the operator can fix the cluster without
95+
// re-running the poll out-of-band.
96+
func TestValidateHTFIFOCapability_RejectsWhenOnePeerLacksCapability(t *testing.T) {
97+
t.Parallel()
98+
99+
good := htfifoCapabilityServer(t, []string{sqsCapabilityHTFIFO})
100+
old := htfifoCapabilityServer(t, []string{}) // pre-htfifo binary
101+
oldAddr := strings.TrimPrefix(old.URL, "http://")
102+
s := &SQSServer{leaderSQS: map[string]string{
103+
"raft1": strings.TrimPrefix(good.URL, "http://"),
104+
"raft2": oldAddr,
105+
}}
106+
107+
err := s.validateHTFIFOCapability(context.Background(), &sqsQueueMeta{PartitionCount: 8})
108+
require.Error(t, err)
109+
110+
var apiErr *sqsAPIError
111+
ok := errors.As(err, &apiErr)
112+
require.True(t, ok, "must surface as sqsAPIError so the wire layer maps to InvalidAttributeValue, got %T", err)
113+
require.Equal(t, http.StatusBadRequest, apiErr.status)
114+
require.Equal(t, sqsErrInvalidAttributeValue, apiErr.errorType)
115+
require.Contains(t, apiErr.message, "every cluster peer to advertise the htfifo capability",
116+
"message must explain the gate so the operator knows what to fix")
117+
require.Contains(t, apiErr.message, oldAddr,
118+
"the offending peer must appear in the message, got %q", apiErr.message)
119+
}
120+
121+
// TestValidateHTFIFOCapability_RejectsWhenPeerUnreachable pins
122+
// the network-failure fail-closed: a peer whose /sqs_health is
123+
// unreachable (closed listener) must block the create. A
124+
// transient network blip during a CreateQueue is exactly the
125+
// class of partial-cluster state the gate is designed to catch —
126+
// silently accepting the create here would let a partitioned
127+
// queue land while a peer is offline and silently drop messages
128+
// the moment that peer comes back as a non-htfifo binary.
129+
func TestValidateHTFIFOCapability_RejectsWhenPeerUnreachable(t *testing.T) {
130+
t.Parallel()
131+
132+
// Bind a port, capture its address, then close the listener
133+
// so dials fail immediately rather than waiting on the
134+
// per-peer timeout.
135+
srv := htfifoCapabilityServer(t, []string{sqsCapabilityHTFIFO})
136+
deadAddr := strings.TrimPrefix(srv.URL, "http://")
137+
srv.Close()
138+
139+
s := &SQSServer{leaderSQS: map[string]string{"raft1": deadAddr}}
140+
141+
err := s.validateHTFIFOCapability(context.Background(), &sqsQueueMeta{PartitionCount: 2})
142+
require.Error(t, err)
143+
var apiErr *sqsAPIError
144+
ok := errors.As(err, &apiErr)
145+
require.True(t, ok)
146+
require.Equal(t, http.StatusBadRequest, apiErr.status)
147+
require.Equal(t, sqsErrInvalidAttributeValue, apiErr.errorType)
148+
require.Contains(t, apiErr.message, deadAddr,
149+
"unreachable peer must be named in the rejection message")
150+
}
151+
152+
// TestCollectSQSPeers_Deterministic pins the helper's order +
153+
// dedup contract: leaderSQS is a map (random Go iteration order),
154+
// but the gate's error message and the poller's per-peer index
155+
// must be deterministic so test assertions, log lines, and
156+
// operator triage are stable across runs.
157+
func TestCollectSQSPeers_Deterministic(t *testing.T) {
158+
t.Parallel()
159+
160+
s := &SQSServer{leaderSQS: map[string]string{
161+
"r1": "node3:9000",
162+
"r2": "node1:9000",
163+
"r3": "node2:9000",
164+
"r4": "node1:9000", // duplicate (two Raft nodes pointing at one SQS endpoint)
165+
"r5": "", // empty string must be skipped
166+
}}
167+
168+
got := s.collectSQSPeers()
169+
require.Equal(t, []string{"node1:9000", "node2:9000", "node3:9000"}, got,
170+
"peers must be sorted, deduped, and free of empty strings")
171+
172+
// Empty leaderSQS: caller relies on len()==0 to skip the
173+
// poll on single-node deployments.
174+
require.Empty(t, (&SQSServer{}).collectSQSPeers())
175+
}
176+
177+
// TestBuildHTFIFOCapabilityRejection_ShapesOperatorMessage pins the
178+
// rejection-message shape so a future refactor cannot accidentally
179+
// truncate the per-peer detail. Each failing peer must contribute
180+
// a "(reason)" suffix; peers that pass do not appear at all.
181+
func TestBuildHTFIFOCapabilityRejection_ShapesOperatorMessage(t *testing.T) {
182+
t.Parallel()
183+
184+
report := &HTFIFOCapabilityReport{
185+
Peers: []HTFIFOCapabilityPeerStatus{
186+
{Address: "ok:9000", HasHTFIFO: true},
187+
{Address: "old:9000", HasHTFIFO: false, Capabilities: []string{}},
188+
{Address: "down:9000", HasHTFIFO: false, Error: "dial tcp: refused"},
189+
},
190+
}
191+
192+
msg := buildHTFIFOCapabilityRejection(report)
193+
require.Contains(t, msg, "every cluster peer to advertise the htfifo capability")
194+
require.NotContains(t, msg, "ok:9000", "advertising peers must NOT appear in the rejection")
195+
require.Contains(t, msg, "old:9000 (missing capability)")
196+
require.Contains(t, msg, "down:9000 (dial tcp: refused)")
197+
198+
// Defensive: nil report and "all-passing-but-AllAdvertise-false" path.
199+
require.Contains(t, buildHTFIFOCapabilityRejection(nil), "no report")
200+
allPass := &HTFIFOCapabilityReport{Peers: []HTFIFOCapabilityPeerStatus{{Address: "x", HasHTFIFO: true}}}
201+
require.Contains(t, buildHTFIFOCapabilityRejection(allPass), "unknown peer",
202+
"never emit a truncated 'did not: ' tail when no peer details surface")
203+
}

adapter/sqs_catalog.go

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -903,13 +903,15 @@ func (s *SQSServer) createQueueCore(ctx context.Context, in *sqsCreateQueueInput
903903
if err != nil {
904904
return "", err
905905
}
906-
// Temporary dormancy gate (Phase 3.D §11 PR 2). PartitionCount > 1
907-
// must reject until PR 5 wires the data plane atomically with the
908-
// gate-lift. Without this, accepting a partitioned-queue create
909-
// would let SendMessage write under the legacy single-partition
910-
// prefix; the PR 5 reader would never find those messages and the
911-
// reaper would not enumerate them — silent message loss.
912-
if err := validatePartitionDormancyGate(requested); err != nil {
906+
// Cluster-wide htfifo capability gate (Phase 3.D §11 PR 5b-3,
907+
// replaces the PR 2 dormancy reject). PartitionCount > 1 is
908+
// rejected unless this binary AND every peer in s.leaderSQS
909+
// advertise the htfifo capability via /sqs_health. The gate
910+
// fails closed on any peer timeout, HTTP error, malformed body,
911+
// or missing capability so a partitioned queue cannot land in a
912+
// partially-upgraded cluster where some peer would silently
913+
// store its records under the legacy single-partition keyspace.
914+
if err := s.validateHTFIFOCapability(ctx, requested); err != nil {
913915
return "", err
914916
}
915917
if len(in.Tags) > sqsMaxTagsPerQueue {

adapter/sqs_partitioned_dispatch_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@ import (
1414
)
1515

1616
// Integration tests for the PR 5b-2 partitioned-FIFO data plane
17-
// wiring. The §11 PR 2 dormancy gate still rejects PartitionCount
18-
// > 1 at CreateQueue (lifted atomically with the capability check
19-
// in PR 5b-3), so these tests reach below the public CreateQueue
20-
// surface to install a partitioned meta record directly. That
21-
// short-circuits the dormancy gate for the duration of the test
22-
// without disabling it for production CreateQueue calls — which
23-
// is the exact split the design doc envisaged for "data plane
24-
// landed but not user-creatable yet."
17+
// wiring. PR 5b-3 has since lifted the §11 PR 2 dormancy gate, so
18+
// CreateQueue with PartitionCount > 1 succeeds on a single-node
19+
// cluster. The helper below predates that gate-lift and overrides
20+
// the meta record directly to keep these tests independent of the
21+
// CreateQueue capability path; new partitioned-FIFO tests should
22+
// prefer the public CreateQueue surface.
2523

2624
// installPartitionedMetaForTest overwrites the queue's meta record
2725
// with a partitioned shape (PartitionCount > 1) by dispatching a
@@ -30,11 +28,13 @@ import (
3028
// counters and the catalog index are populated correctly); only
3129
// the partition-shape attributes are mutated.
3230
//
33-
// The dormancy gate intercepts CreateQueue, not the data plane,
34-
// so once the meta record carries PartitionCount > 1 every
35-
// SendMessage / ReceiveMessage / DeleteMessage call routes
36-
// through the partitioned dispatch helpers exactly as it would
37-
// after PR 5b-3 lifts the gate.
31+
// Predates the PR 5b-3 gate-lift; still useful for tests that want
32+
// to bypass the capability poll (e.g. when CreateQueue would have
33+
// to negotiate over a fake peer list). Once the meta record
34+
// carries PartitionCount > 1, every SendMessage / ReceiveMessage /
35+
// DeleteMessage call routes through the partitioned dispatch
36+
// helpers exactly as it would after a normal partitioned-queue
37+
// CreateQueue.
3838
func installPartitionedMetaForTest(t *testing.T, node Node, queueName string, partitionCount uint32, throughputLimit string) {
3939
t.Helper()
4040
s := node.sqsServer

0 commit comments

Comments
 (0)