Cross-replica cache invalidation without distributed locks

TODO: Write full article. Outline below — all sections are ready to expand.

The problem

Fly.io scales horizontally. Two machines in different regions means two independent in-process IMemoryCache instances. When Machine A invalidates a cache entry, Machine B has no idea it happened.

The naive solution — distributed locks — adds latency to every write path and introduces a new failure mode (lock server unavailability).

The solution: a Redis key registry

Code walkthrough

Walk through ListingCacheInvalidator.csTrackKey, InvalidateAllAsync, thread safety with lock (_lock), and why ConfigureAwait(false) matters here.

The tradeoff I accepted

Fire-and-forget means: if Redis is temporarily unreachable, keys created on Machine A during the outage won’t propagate to Machine B’s invalidation run. In practice this means a stale window of at most one TTL cycle. For a rental listings API (listings update infrequently), this is acceptable. For financial data it would not be.

What I’d do differently

If consistency were stricter — use Redis Pub/Sub to push invalidation events rather than polling the registry. This would close the stale window entirely.