Skip to content

PATTERN Cited by 1 source

Route-tagged query isolation

Pattern: tag every database query with the HTTP route (endpoint pattern) that triggered it, via middleware injection at request-acceptance time, so per-endpoint resource budgets can prevent a heavy endpoint from degrading the rest of the application. The /api/export CSV-report endpoint must not be able to kill the /api/checkout flow.

Shape (Go)

One HTTP middleware + the context-threaded tag propagation substrate (Source: sources/2026-04-21-planetscale-patterns-for-postgres-traffic-control):

func SQLTagMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        tags := tagsFromContext(r.Context())
        route := strings.ReplaceAll(strings.ReplaceAll(r.Pattern, "{", ":"), "}", ":")
        tags["route"] = route
        tags["app"] = "web"
        ctx := contextWithTags(r.Context(), tags)
        next.ServeHTTP(w, r.WithContext(ctx))
    })
}

The { / }: replacement normalises Go 1.22+ net/http patterns (/api/users/{id}) into a shape that's usable as a budget-rule key (/api/users/:id:) without breaking either system — each endpoint collapses to a stable, bounded-cardinality tag value.

What each tag buys

  • route=... is the per-endpoint dial. Define route='/api-export' with a conservative CPU limit + short per-query cap; the endpoint stays usable but can't monopolise.
  • app='web' is a coarse stack-tier tag that distinguishes web-tier traffic from job-tier / script-tier traffic in the same cluster (complement to patterns/dedicated-application-name-per-workload).

Budgets can match on route alone, app alone, or the pair; both tags ride on every query on the web tier.

When to use it

The pattern is the single most operationally valuable tagging axis for a monolith or large API service because the typical failure mode is "one endpoint started scanning too much", not "the whole service is broken". Specific triggers:

  • Reports / exports / bulk-operation endpoints that do more work than typical CRUD handlers.
  • Admin endpoints that may scan without LIMIT.
  • Search / aggregation endpoints whose cost is query-dependent rather than constant.
  • Legacy endpoints under deprecation that shouldn't expand their footprint as usage grows elsewhere.

Composes with other axes

The full power is in composition (concepts/composable-tag-axes). A budget rule matching tier='free' AND route='api-export' is strictly tighter than either alone — enterprise customers' exports can have headroom that free-tier exports can't.

Incident-response payoff

Canonical operational framing (Source: sources/2026-04-21-planetscale-patterns-for-postgres-traffic-control): "This also makes it easy to set up broad budgets during incidents. If you suddenly see a spike and don't know which route is responsible, the violation graph in Traffic Control will show you exactly which route tag is hitting limits."

The pattern turns "which endpoint is the problem?" from a log-join problem into a dashboard click. The same tag that enforces the budget is the tag you filter on for attribution.

Operational notes

  • Route pattern ≠ route URL. Middleware must pull the router's pattern (/api/users/{id}), not the path (/api/users/12345). Using the path blows up tag cardinality by the number of distinct IDs; using the pattern stays bounded.
  • Character normalisation matters. Brace characters in patterns confuse some rule-matcher parsers; the canonical { / }: replacement is the safe default.
  • Middleware must wrap all handlers. Any handler not wrapped emits untagged SQL; budgets don't apply. Best practice: wrap at the root mux, not per-handler.
  • Nested mux / sub-router routing may require the middleware to run at each router layer; verify r.Pattern reflects the final route match.

Seen in

Last updated · 378 distilled / 1,213 read