Skip to content

Label Matching Strategy

This document describes how the Hub resolves a requested label key to an available capacity pool using a central, pluggable strategy.

Labels are ordered, ':'-separated keys. We recommend keeping Browser as the second segment for consistency: - App:Browser:Env[:Region[:OS[:...]]]

Overview

The matcher evaluates candidates in the following order:

  1. Exact match
  2. Same number of segments and segment-by-segment equality.
  3. Trailing fallback (optional)
  4. Progressively drop trailing segments from the request down to a minimum (default 2 → App:Browser).
  5. Prefix expansion (optional)
  6. If no exact or fallback match is found, accept more specific available labels whose leading segments match the request.
  7. Among multiple matches, pick the shortest candidate (fewest extra segments); break ties lexicographically by normalized label.
  8. Wildcards (optional)
  9. If enabled and the request contains *, a * in any segment matches any single segment during exact/prefix phases.

If no candidate matches, the Hub returns 503 (capacity unavailable).

Deterministic tie‑breaking

When multiple candidates match in the same phase, the matcher picks deterministically: - Prefer the candidate with the fewest segments beyond the requested prefix (shortest). - If equal length, choose the lexicographically first by normalized label (ordinal comparison).

Configuration knobs

Hub environment variables controlling the strategy: - HUB_BORROW_TRAILING_FALLBACK=true | false - HUB_BORROW_PREFIX_EXPAND=true | false - HUB_BORROW_WILDCARDS=false | true

Defaults: trailing fallback and prefix expansion enabled; wildcards disabled.

Per-environment overrides: - You can scope each flag per environment by appending _{EnvironmentName}, e.g., HUB_BORROW_WILDCARDS_Production=true. - The Hub resolves the effective value as: specific environment-suffixed key (case-insensitive on suffix) → base key → default.

Domain API

The strategy is centralized in the Domain package: - LabelKey: value object for parsing/validation/normalization of label keys. - LabelMatchingOptions: enables/disables trailing fallback, prefix expansion, and wildcards; also sets MinSegmentsForFallback (default 2). - ILabelMatcher / LabelMatcher: ordered strategy implementation (exact → trailing → prefix, with optional wildcards) and deterministic tie‑breaking.

Example usage:

var options = new LabelMatchingOptions
{
    TrailingFallbackEnabled = true,
    PrefixExpansionEnabled = true,
    WildcardsEnabled = false,
    MinSegmentsForFallback = 2
};
var matcher = new LabelMatcher(options);

LabelKey.TryParse("AppA:Chromium:UAT", out var requested);
var available = new[] { "AppA:Chromium", "AppA:Chromium:UAT", "AppA:Chromium:UAT:EU" }
    .Select(s => LabelKey.TryParse(s, out var lk) ? lk! : null)
    .Where(lk => lk is not null)!;

var match = matcher.TryMatch(requested!, available!);
// match.Normalized == "AppA:Chromium:UAT"

Wildcards and parsing

When HUB_BORROW_WILDCARDS is true and the requested label contains *, parsing relaxes the default enforcement that the second segment must be a known browser. The Hub uses: - LabelKey.TryParse(request, new LabelKeyParsingOptions { EnforceBrowserSecond = false }) This allows * in any segment, including the Browser position.

Examples (with wildcards enabled): - Request App:*:UAT matches App:Chromium:UAT or App:Firefox:UAT (exact), before considering prefix expansion. - Request *:Firefox:Staging matches any App with Firefox:Staging pools.

Examples

  1. Exact match
  2. Requested: AppA:Chromium:UAT
  3. Available: AppA:Chromium:UAT, AppA:Chromium
  4. Chosen: AppA:Chromium:UAT (exact; same number of segments and equality per segment).
  5. Trailing fallback (optional)
  6. Requested: AppA:Chromium:UAT:EU
  7. Available: AppA:Chromium:UAT, AppA:Chromium
  8. Chosen: AppA:Chromium:UAT (drop trailing EU to the first available match; do not drop below App:Browser by default).
  9. Prefix expansion (optional)
  10. Requested: AppB:Firefox
  11. Available: AppB:Firefox:UAT, AppB:Firefox:Prod:EU
  12. Chosen: AppB:Firefox:UAT (accept more specific labels; pick the shortest candidate; break ties lexicographically).
  13. Wildcards (optional)
  14. Requested: AppA:*:UAT (wildcards enabled)
  15. Available: AppA:Firefox:UAT, AppA:Chromium:UAT:EU
  16. Chosen: AppA:Firefox:UAT (exact with wildcard beats longer prefix expansion).

Operational notes

  • Keep Browser as second segment across all labels for consistency in routing, metrics, and UI filters.
  • Choose a clear case policy and segment vocabulary for your organization; LabelKey supports normalization options.
  • Avoid excessive cardinality (too many distinct labels) to keep metrics and dashboards efficient.
  • Session distribution across workers: docs/distribution.md
  • Testing and environment knobs: see docs/TestClient-Usage.md and tests/TestEnvironment.cs