Skip to content

CONCEPT Cited by 1 source

Composable tag axes

Composable tag axes is the design property of a query- tagging rule engine that allows multiple orthogonal tag dimensions to coexist on the same query, with budget rules able to match on any single axis or any AND-combination of axes. Every query carries a tuple of tag values, not a single classification label; a rule matches a query if the query satisfies the rule's tag predicates.

The canonical realisation is PlanetScale Traffic Control's budget engine, where a single query can simultaneously carry — and be governed by — up to five or more independent axes.

The five canonical axes

The canonical five axes from the Brown post (Source: sources/2026-04-21-planetscale-patterns-for-postgres-traffic-control):

Axis Source Example tag
Service Postgres username (dedicated role per service) or application_name connection-string parameter username='pscale_api_123abc'
Route HTTP middleware reads r.Pattern route='/api/checkout'
Deployment Startup env var (DEPLOYMENT_TAG=...) feature='new_checkout_v2'
Tier Auth middleware reads user.Tier tier='FREE'
Workload Dedicated connection pool per workload application_name='background-jobs'

These are orthogonal — any one query from a web handler processing a free-tier user's checkout request on the canary deployment could carry all five tags simultaneously:

SELECT ... /* app='web', route='/api/checkout',
feature='new_checkout_v2', tier='FREE',
application_name='pscale_api_123abc' */

AND-composition semantics

The load-bearing rule-engine property: multiple matching budgets all apply simultaneously and queries must satisfy every budget they match (Source, verbatim). A query tagged tier='free' and route='api-export' is subject to:

  • The tier='free' budget (all free-tier traffic)
  • The route='api-export' budget (all export-endpoint traffic)
  • The tier='free' AND route='api-export' budget (specifically free-tier export traffic, typically stricter than either alone)

All three caps apply at once; the query must pass all three to not be rejected. The strictest applicable rule wins — or more precisely, every applicable rule is independently enforced, so the query experiences the intersection of resource limits.

Why orthogonal axes beat single-label classification

A classification-based alternative would require every team to agree on a single taxonomy axis (priority? workload-type? tier?) and project their concern onto it — losing information the other axes captured. Orthogonal axes preserve independent concerns without requiring cross-team coordination:

  • The platform team sets application_name per service (Pattern 1) without coordinating with…
  • …the web team that sets route in middleware (Pattern 2), which doesn't need to know about…
  • …the release team that sets feature=<deployment-tag> at startup (Pattern 3), which is independent of…
  • …the billing team that sets tier in auth middleware (Pattern 4), which doesn't affect…
  • …the jobs team's dedicated application_name per job pool (Pattern 5).

Each team owns one axis, defines budgets on it, and compositions happen at rule-authoring time without schema coordination.

Canonical framing

Verbatim (Source: sources/2026-04-21-planetscale-patterns-for-postgres-traffic-control):

"Traffic Control sees all of this as a combination of tags. A budget on tier='free' covers all free-tier traffic regardless of route. A budget on route='api-export' AND tier='free' covers a specific combination. Multiple matching budgets all apply simultaneously and queries must satisfy every budget they match. You can build layered policies without complicated rule logic."

"Layered policies without complicated rule logic" is the canonical wiki framing of what orthogonal axes buy — each team defines its rules independently, composition is automatic.

Comparison with priority-only classification

The 2026-03-31 Dicken graceful-degradation post canonicalised a priority-tier-only tagging scheme (critical / important / best-effort). That's one axis. The Brown post extends to priority + four more axes — priority is still meaningful, but it's one dimension among many. A query can be priority='critical' AND tier='free' AND route='/api/post' simultaneously, with budgets potentially defined on each axis or any combination.

Canonical relationship: priority classification is one axis; composable tag axes is the meta-framework that lets priority coexist with orthogonal axes the app team cares about.

Design constraints

  • Axes must be orthogonal in meaning. Two axes that are near-synonyms (e.g. service and application_name if the team maps them identically) produce redundant budgets without analytical value.
  • Cardinality per axis matters for observability. Tag cardinality explosion is its own failure mode — see concepts/per-pattern-tag-cardinality and patterns/dynamic-cardinality-reduction-by-tag-collapse. Each axis should have bounded, operator-meaningful values (avoid free-text user_id, request_id as budget-keying axes).
  • Axis-writer layers must not fight. Middleware that overwrites another middleware's tag value destroys the compositional property. Canonical convention: read-modify- write — each middleware calls tagsFromContext, adds its key, calls contextWithTags with the merged map.

Seen in

Last updated · 378 distilled / 1,213 read