core.strangler_router module

DEPRECATED (Phase T3, 2026-06-02) — retained for historical reference.

The strangler-fig router used to decide, per channel/guild, whether a message was handled by the legacy monolith or the new microservices. Now that the monolith is gone and all traffic routes over the event bus, StranglerRouter is no longer instantiated by any service. Do not import or use it in new code; it is documented only to explain the migration history.

class core.strangler_router.StranglerRouter(redis)[source]

Bases: object

DEPRECATED strangler-fig router for monolith-vs-microservice traffic.

During the migration this decided, per channel / guild / platform, whether an inbound message was handled by the legacy monolith or the new microservices, using a cached rule hierarchy plus a percentage rollout knob so traffic could be shifted gradually and safely. As of Phase T3 (2026-06-02) the monolith is gone and all traffic routes over the event bus, so no live service constructs this class – it is retained only to document the migration and is exercised solely by tests/core/migration/test_strangler_router.py.

Reads its rules from the sg:strangler:routes Redis hash, subscribes to the sg:pubsub:config channel to invalidate its in-memory cache when an operator script (scripts/canary_rollout.py, scripts/enable_shadow_mode.py) republishes routes, and shields the cache-miss path with a CircuitBreaker so a flaky Redis falls back to a safe default rather than failing routing. Do not import or instantiate it in new code.

__init__(redis)[source]

Initialize the router with a Redis handle and an empty route cache.

Sets up the in-memory _cache dict that holds routing rules pulled from Redis, a CircuitBreaker (failure_threshold=3, reset_timeout=30.0) guarding the cache-miss path against a flaky Redis, an unset _listen_task handle for the background invalidation listener, and a _default_route of "monolith" used as the safe fallback.

This only stores the passed redis handle and constructs the circuit breaker; it performs no I/O, so the cache is populated lazily on the first _fetch_route() call. Since the module is deprecated (Phase T3) no live service constructs this class; the only caller is the test suite (tests/core/migration/test_strangler_router.py).

Parameters:

redis – An async Redis client (e.g. redis.asyncio.Redis) used for hgetall of the routes hash and for pub/sub subscription to config-change notifications.

async start()[source]

Launch the background cache-invalidation listener.

Schedules _listen_for_invalidations() as a fire-and-forget asyncio task and stores its handle on self._listen_task so stop() can cancel it later. Returns immediately without awaiting the listener.

This calls asyncio.create_task(); the spawned task subscribes to the sg:pubsub:config Redis channel and clears self._cache on any published message. Historically called by GatewayService during startup; as of Phase T3 there are no live callers (the router was removed from the gateway), so only the test suite exercises it.

async stop()[source]

Cancel and join the background cache-invalidation listener.

Idempotent teardown counterpart to start(): if a listener task is running it is cancelled and awaited, swallowing the resulting asyncio.CancelledError so shutdown completes cleanly. When no task was ever started this is a no-op.

This cancels the task created by start() (which holds the sg:pubsub:config pub/sub subscription). Historically called by GatewayService during shutdown; as of Phase T3 only the test suite invokes it.

async get_route(channel=None, guild=None, platform=None)[source]

Resolve the route for a message, failing safe on any error.

Public entry point of the router: it delegates the real decision to _fetch_route() but runs it inside the instance’s CircuitBreaker so a Redis outage during a cache refresh trips the breaker instead of stalling every message. On either an open breaker or any other exception it logs and returns the safe self._default_route ("monolith"), so routing degrades rather than throws.

Touches Redis only indirectly, through _fetch_route() / _refresh_cache() reading sg:strangler:routes. Since the module is deprecated (Phase T3) it has no live callers; only tests/core/migration/test_strangler_router.py invokes it.

Parameters:
  • channel (str) – Optional channel identifier; highest-priority override.

  • guild (str) – Optional guild/server identifier; second-priority override.

  • platform (str) – Optional platform name (e.g. "discord"); third-priority override.

Returns:

The resolved route (typically "microservice" or "monolith"), falling back to self._default_route on breaker open or error.

Return type:

str