perf(proxy): cache LicenseService tier headers for the proxy hot path (#815)

The remote-node HTTP proxy and WebSocket forwarder read getTier() +
getVariant() on every forwarded request to set the Distributed License
Enforcement headers. Each call hits system_state 5+ times. Add a
30-second cached snapshot inside LicenseService and route every
license_status write through a new private setLicenseStatus() helper
so activate, deactivate, validate, and the auto-demote paths inside
getTier() all invalidate the cache.

Routing all license_status writes through one chokepoint also closes
a latent drift window: the self-heal paths in getTier() (trial
expired, offline grace exceeded, subscription expired) used to mutate
state silently and now invalidate the cache the same way explicit
license events do.

The TTL becomes a safety net against any future write that bypasses
the helper, not a load-bearing freshness bound. Existing 44 license
and distributed-license tests pass unchanged.
This commit is contained in:
Anso
2026-04-28 00:13:07 -04:00
committed by GitHub
parent 836e384d17
commit 61a7e43d82
3 changed files with 57 additions and 19 deletions
+6 -4
View File
@@ -48,10 +48,12 @@ export function createRemoteProxyMiddleware(): RequestHandler {
// tier to the remote node so tier-gated routes honor the main's
// license instead of the node's local (likely Community) tier. The
// remote's authMiddleware only trusts these headers when the request
// carries a valid node_proxy JWT.
const proxyLs = LicenseService.getInstance();
proxyReq.setHeader(PROXY_TIER_HEADER, proxyLs.getTier());
proxyReq.setHeader(PROXY_VARIANT_HEADER, proxyLs.getVariant() || '');
// carries a valid node_proxy JWT. The cached snapshot here invalidates
// on activate / deactivate / validate so the headers track license
// state changes within one proxy call.
const headers = LicenseService.getInstance().getProxyHeaders();
proxyReq.setHeader(PROXY_TIER_HEADER, headers.tier);
proxyReq.setHeader(PROXY_VARIANT_HEADER, headers.variant || '');
// Strip the ?nodeId= query param so the remote's nodeContextMiddleware
// doesn't reject the request with 404 ("Node X not found") - the remote
// has no record of the gateway's node IDs and should treat the request