-
Notifications
You must be signed in to change notification settings - Fork 2k
chore(flags): Encapsulate caching patterns with a ReadThroughCache
#40751
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: haacked/redis-client-ztsd
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
This PR introduces a new generic read-through cache crate (common-cache) for PostHog services with Redis-based positive caching and optional in-memory negative caching. It also fixes a lifetime elision issue in the MockRedisClient.
- Adds a comprehensive read-through cache implementation with Redis and Moka-based negative caching
- Provides rich observability through CacheSource enum to track cache hits, misses, and infrastructure issues
- Includes a minor fix to explicitly specify the lifetime parameter in MockRedisClient's lock_calls method
Reviewed Changes
Copilot reviewed 7 out of 8 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| rust/common/redis/src/lib.rs | Fixes lifetime elision by explicitly adding '_ to MutexGuard return type |
| rust/common/cache/src/read_through.rs | Core read-through cache implementation with Redis positive caching and fallback logic |
| rust/common/cache/src/negative_cache.rs | In-memory negative cache using Moka for preventing repeated DB queries for missing keys |
| rust/common/cache/src/lib.rs | Module exports and crate-level documentation |
| rust/common/cache/src/config.rs | Cache configuration and result types with helper methods for observability |
| rust/common/cache/Cargo.toml | Package definition for the new common-cache crate |
| rust/Cargo.toml | Adds common-cache to workspace members |
| rust/Cargo.lock | Lock file updates for new dependencies |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
a14c2d2 to
9cbb68a
Compare
452143a to
d0277d4
Compare
9cbb68a to
81f1181
Compare
We have a lot of places where we attempt to read a Redis cache entry and if the entry doesn't exist, we fetch the data from the database and then write the entry to a writer `RedisClient`. The new `ReadThroughCache` class encapsulates and improves on that pattern. For example, our existing code: 1. Treats `TimeoutError` and other errors as cache misses, causing us to attempt to write to the cache when it might be under load. 2. Doesn't evict existing entries when the DB fetch produces no results. 3. Doesn't distinguish between corrupt data and missing data. 4. Doesn't cache negative entries. So if a project api key doesn't exist, requests with it always hit the db. The `ReadThroughCache` (combined with `NegativeCache`) provides the structure to fix all these issues.
81f1181 to
ceddacc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
7 files reviewed, no comments
This PR targets the
haacked/redis-client-ztsdbranch as it needs those changes to be merged first.Problem
Feature flags has a lot of places where it reads from cache, and in case of cache miss, writes to cache. But it isn't consistent in how it does it and the current approach has some problems such as trying to write when Redis is timing out.
We need a consistent approach for caching that:
Currently, our cache handling is scattered across services with inconsistent error handling and limited observability.
Solution:
ReadThroughCacheincommon-cacheA new generic read-through cache abstraction that encapsulates cache patterns and provides rich metadata for observability. The cache works with any async loader function (databases, HTTP APIs, file systems, computations, etc.), making it reusable across all PostHog services. I separated this out into its own thing to make it easier to review.
Key Components
Usage Examples
Basic Usage
With Negative Caching
CacheResultHelper MethodsThe
CacheResultprovides convenience methods for common checks:Cache Write Behavior
The cache writes to Redis on true cache miss or on parse errors.
Important
The goal: when Redis is struggling, we don't add to its load with writes.
Implementation Details
Mokacache withLRUeviction andTTL(doesn't poison Redis)CacheResult<V>works with any serializable typeCacheSourceenum makes it easy to verify behavior in testsHow did you test this code?
Next Steps
Future PRs will migrate feature-flags service to use
ReadThroughCachefor:This infrastructure can also be adopted by other services (capture, data-stack, etc.) for consistent caching behavior across PostHog.