PATTERN Cited by 1 source
Tier-tagged query isolation¶
Pattern: in a SaaS application with differentiated
subscription tiers, tag every database query with the
authenticated user's tier (e.g. tier='FREE' / 'PRO' /
'ENTERPRISE') at authentication-middleware time, and define
per-tier resource
budgets so that lower-tier traffic cannot degrade higher-tier
experience. Enforces the SLA differential at the database
tier rather than relying solely on application-layer rate
limits.
Shape (Go)¶
Three components (Source: sources/2026-04-21-planetscale-patterns-for-postgres-traffic-control):
(1) Tier type + context setter:
type Tier string
const (
TierFree Tier = "FREE"
TierPro Tier = "PRO"
TierEnterprise Tier = "ENTERPRISE"
)
func WithUserTier(ctx context.Context, tier Tier) context.Context {
tags := tagsFromContext(ctx)
tags["tier"] = string(tier)
return contextWithTags(ctx, tags)
}
(2) Authentication middleware that resolves the user and injects the tier:
func AuthMiddleware(users *UserService, next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, err := users.Authenticate(r)
if err != nil { http.Error(w, "unauthorized", 401); return }
ctx := WithUserTier(r.Context(), user.Tier)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
(3) Per-tier Traffic Control budgets:
tier='FREE'— conservativeServer share+ lowmax concurrent workers.tier='PRO'— moderate limits.tier='ENTERPRISE'— unbudgeted, or a high ceiling as a runaway-protection safety net.
The pattern rides on the context-threaded tag propagation substrate — tier joins whatever other tags other middlewares added.
Why database-tier enforcement¶
Application-layer rate limits (token buckets on request count) can't distinguish a cheap query from an expensive one. A free-tier user who triggers a slow 500-row JOIN consumes more database CPU than an enterprise user's fast indexed lookup — but both count as "one request" at the rate limiter.
Tier tags + Traffic Control budgets measure at the resource level (CPU, I/O, worker-process concurrency) rather than the request level. A free-tier user whose query is expensive is throttled on exactly the resource they're consuming excessively, and the throttle kicks in at the database extension layer — no application code change needed.
Compositions¶
The pattern is most valuable when composed with route tags (patterns/route-tagged-query-isolation). Canonical worked composition (Source: sources/2026-04-21-planetscale-patterns-for-postgres-traffic-control):
"A budget matching
tier='free' AND route='api-export'can be stricter than a budget ontier='free'alone. Enterprise export requests get more headroom than free-tier export requests."
Three independent rule levels coexist:
tier='FREE'— baseline free-tier budget.route='/api/export'— baseline expensive-endpoint budget.tier='FREE' AND route='/api/export'— the AND-combination is tighter than either alone.
All three apply simultaneously via AND-composition; a free-tier export request must satisfy all three caps.
Under spike: live-disable lower tiers¶
Paired with spike-
survival shedding, the tier axis becomes the load-shedding
lever: under a load spike, operators can disable the
tier='FREE' budget entirely from the Traffic Control UI —
free-tier traffic is blocked, enterprise and pro keep running.
Same mechanism the priority-tier framing uses, specialised to
subscription plans.
Operational notes¶
- Auth middleware must run before DB access. The tier tag
must be in context before the first query runs. Unauthenticated
endpoints (login, signup) won't carry a tier tag; budgets
targeting
tier=*won't catch them — use a separateroutebudget or anapplication_name='web'budget for these paths. - Tier transitions during a session. If a user upgrades mid-session, the tier in context reflects the user object at authentication time. For mid-request upgrade semantics, re-authenticate or update the context explicitly.
- Workspace / organisation-level tiering adds a dimension: a single user may be in multiple workspaces at different tiers. The tag should reflect the effective tier for the current request — typically the workspace's, not the user's.
- Internal service callers (machine-to-machine) need their
own tag (
tier='INTERNAL'orapp='internal') to avoid being caught by user-tier budgets.
Seen in¶
- sources/2026-04-21-planetscale-patterns-for-postgres-traffic-control — canonical wiki introduction. Josh Brown's Pattern 4 with the three-component auth-middleware shape and the tier + route AND-composition framing.