Skip to content

Commit 7c31a34

Browse files
fix(bugbash 2026-05-21): wave-1 partial — Redis k8s sizingForTier yearly + plus tiers (#9)
Covers: - E32 F1 [P1]: explicit cases for hobby_yearly, hobby_plus, hobby_plus_yearly, pro_yearly, team_yearly. Previously all fell through to default (hobby 50MB). pro_yearly now gets 512MB; team_yearly is unlimited. Remaining P0/P1: Mongo authSource align (E33 F1), Neon token truncation + idempotency (E31 F1), storage_admin breaker (E31 F5), Mongo k8s hobby_plus case (E33 F2), Mongo localBackend UserAlreadyExists idempotency (E33 F5). Co-authored-by: Manas Srivastava <[email protected]> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 1d19390 commit 7c31a34

2 files changed

Lines changed: 29 additions & 5 deletions

File tree

internal/backend/redis/k8s.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,8 @@ func sizingForTier(tier string) tierSizing {
176176
maxClients: 10,
177177
maxmemoryMB: 5, // plans.yaml: anonymous redis_memory_mb = 5
178178
}
179-
case "hobby":
179+
case "hobby", "hobby_yearly":
180+
// hobby_yearly mirrors hobby (plans.yaml: identical limits, annual billing).
180181
return tierSizing{
181182
cpuReq: "100m", memReq: "128Mi",
182183
cpuLim: "500m", memLim: "512Mi",
@@ -186,7 +187,24 @@ func sizingForTier(tier string) tierSizing {
186187
maxClients: 50,
187188
maxmemoryMB: 50, // plans.yaml: hobby redis_memory_mb = 50
188189
}
189-
case "pro":
190+
case "hobby_plus", "hobby_plus_yearly":
191+
// hobby_plus (W11 mid-tier insertion 2026-05-13). Redis memory cap
192+
// matches hobby (50MB) per plans.yaml; the upsell over hobby is on
193+
// postgres/mongo/storage, not redis. F1 fix (2026-05-21): explicit
194+
// case so this tier no longer falls through to the default → hobby
195+
// path, which by coincidence had the same maxmemoryMB but would
196+
// silently drift the moment hobby_plus diverged from hobby.
197+
return tierSizing{
198+
cpuReq: "100m", memReq: "128Mi",
199+
cpuLim: "500m", memLim: "512Mi",
200+
pvcMi: 1024, // 1Gi (matches hobby)
201+
qCPURequests: "200m", qMemRequests: "256Mi",
202+
qCPULimits: "1", qMemLimits: "1Gi",
203+
maxClients: 50,
204+
maxmemoryMB: 50, // plans.yaml: hobby_plus redis_memory_mb = 50
205+
}
206+
case "pro", "pro_yearly":
207+
// pro_yearly mirrors pro (plans.yaml: identical limits, annual billing).
190208
return tierSizing{
191209
cpuReq: "250m", memReq: "512Mi",
192210
cpuLim: "2", memLim: "2Gi",
@@ -206,7 +224,8 @@ func sizingForTier(tier string) tierSizing {
206224
maxClients: 1000,
207225
maxmemoryMB: 1024, // plans.yaml: growth redis_memory_mb = 1024
208226
}
209-
case "team":
227+
case "team", "team_yearly":
228+
// team_yearly mirrors team (plans.yaml: identical -1 limits, annual billing).
210229
return tierSizing{
211230
cpuReq: "500m", memReq: "1Gi",
212231
cpuLim: "4", memLim: "4Gi",

internal/backend/redis/k8s_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,13 @@ func TestSizingForTier_MaxmemoryMB_MatchesPlansYAML(t *testing.T) {
6060
}{
6161
{"anonymous", 5, true}, // plans.yaml: anonymous redis_memory_mb = 5
6262
{"hobby", 50, true}, // plans.yaml: hobby redis_memory_mb = 50
63+
{"hobby_yearly", 50, true}, // plans.yaml: hobby_yearly mirrors hobby
64+
{"hobby_plus", 50, true}, // plans.yaml: hobby_plus redis_memory_mb = 50
65+
{"hobby_plus_yearly", 50, true}, // plans.yaml: hobby_plus_yearly mirrors hobby_plus
6366
{"pro", 512, true}, // plans.yaml: pro redis_memory_mb = 512
67+
{"pro_yearly", 512, true},// plans.yaml: pro_yearly mirrors pro
6468
{"team", -1, false}, // unlimited — flag omitted
69+
{"team_yearly", -1, false}, // plans.yaml: team_yearly mirrors team (unlimited)
6570
{"growth", 1024, true}, // plans.yaml: growth redis_memory_mb = 1024
6671
{"unknown", 50, true}, // unknown → hobby fallback
6772
}
@@ -123,7 +128,7 @@ func TestSizingForTier_MaxmemoryFlag_InCommand(t *testing.T) {
123128
return ""
124129
}
125130

126-
limitedTiers := []string{"anonymous", "hobby", "pro", "growth"}
131+
limitedTiers := []string{"anonymous", "hobby", "hobby_yearly", "hobby_plus", "hobby_plus_yearly", "pro", "pro_yearly", "growth"}
127132
for _, tier := range limitedTiers {
128133
t.Run("limited/"+tier, func(t *testing.T) {
129134
sz := sizingForTier(tier)
@@ -142,7 +147,7 @@ func TestSizingForTier_MaxmemoryFlag_InCommand(t *testing.T) {
142147
})
143148
}
144149

145-
unlimitedTiers := []string{"team"}
150+
unlimitedTiers := []string{"team", "team_yearly"}
146151
for _, tier := range unlimitedTiers {
147152
t.Run("unlimited/"+tier, func(t *testing.T) {
148153
sz := sizingForTier(tier)

0 commit comments

Comments
 (0)