Skip to content
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

Redis cache impl #954

Merged
merged 9 commits into from
Jul 25, 2022
Merged

Redis cache impl #954

merged 9 commits into from
Jul 25, 2022

Conversation

markphelps
Copy link
Collaborator

Fixes: #633 (kind of)

Major Changes

  • Changes caching to be done at the request/response layer instead of the storage layer (using GRPC interceptors/middleware)
  • Adds a Redis cache implementation
  • Deprecates cache.memory.enabled and cache.memory.expiration
  • Display warnings/deprecations in console at startup
  • Ping database on startup to check if it's alive
  • Default cache TTL is 1m. Previously there was no TTL for the in memory cache
  • Support for pretty printing JSON responses from API (via ?pretty=true or setting Accept: application/json+pretty header)
  • New cache config
  • RequestID setting/Response time setting now happens in middleware for evaluation

Caching Strategy

  • Still using a read-through caching strategy
  • For the most part we now just rely on TTL to auto evict stale cache entries
  • Cache keys are based on the request, see here for the logic

Flags

  1. Cache after first flag get request for a specific flag key
  2. Evict from cache on flag updates/deletes
  3. Evict flag from cache on any variant additions/updates/deletes

Evaluation

  1. Cache after evaluation
  2. Eviction ONLY occurs because of TTL since its too complex to try and compute the graph for what would constitute manually eviction evalution entries because of other changes (ie segment update, distribution deletion, etc)

Files to 👀

These are the main files that have major changes that I would love some eyes on in this review

@codecov-commenter
Copy link

codecov-commenter commented Jul 21, 2022

Codecov Report

Merging #954 (eb1af1e) into main (d507232) will decrease coverage by 1.65%.
The diff coverage is 62.20%.

@@            Coverage Diff             @@
##             main     #954      +/-   ##
==========================================
- Coverage   81.18%   79.53%   -1.66%     
==========================================
  Files          19       17       -2     
  Lines        1802     1798       -4     
==========================================
- Hits         1463     1430      -33     
- Misses        268      296      +28     
- Partials       71       72       +1     
Impacted Files Coverage Δ
server/server.go 0.00% <0.00%> (-71.43%) ⬇️
server/middleware.go 41.81% <41.81%> (ø)
server/cache/redis/cache.go 75.60% <75.60%> (ø)
config/config.go 89.68% <94.64%> (+1.81%) ⬆️
server/cache/memory/cache.go 100.00% <100.00%> (ø)
server/evaluator.go 91.69% <100.00%> (+0.49%) ⬆️

Help us with your feedback. Take ten seconds to tell us how you rate us.

Copy link
Contributor

@tsickert tsickert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Left a few questions, found a typo or 2, but the core functionality looks great--this is an awesome new feature!

I don't have a lot of familiarity with the flag evaluation/middleware parts of the code yet, but looked good from my understanding.

🚀

Comment on lines +207 to +209
TTL: 1 * time.Minute,
Memory: MemoryCacheConfig{
Enabled: false,
Expiration: -1,
EvictionInterval: 10 * time.Minute,
EvictionInterval: 5 * time.Minute,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clarification: TTL replaces expiration here and expiration + evicition interval are a gocache paradigm? The Redis cache accepts TTL and uses it to refresh the cache when there's a read + TTL expiration?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TTL replaces expiration here and expiration

Yeah so TTL = expiration from go-cache terminology. Previously the default config used -1 for expiration which meant items never expired automatically. Since now we are more heavily reliant on TTL since we're moving the caching logic up a layer, I figured it made sense to set a 'sane' 1m default instead across the board.

evicition interval are a gocache paradigm

Yeah. go-cache lets you set how often the 'cleanup' routine runs to deleted expired items from the cache, unlike redis (see below) which seems to manage this process it self

Redis cache accepts TTL and uses it to refresh the cache when there's a read + TTL expiration

Yeah so when we go to read from Redis and it sees that the TTL has expired it acts as a 'miss' and so we go read from the DB and then repopulate the cache item. Redis randomly also deletes expired items from the redis db to clear space, but it doesnt auto-repopulate the cache itself: https://redis.io/commands/expire/#how-redis-expires-keys

config/local.yml Outdated Show resolved Hide resolved
@@ -160,10 +202,17 @@ func Default() *Config {
},

Cache: CacheConfig{
Enabled: false,
Backend: CacheMemory,
TTL: 1 * time.Minute,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know if redis allows negative TTLs like in-memory cache?

Question behind the question: do you generally want to defer to the underlying library for configuration validation?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. looking at the lib it seems to return 0 if the TTL is < 0

In general I think we do do more validation on the config after parsing. There is currently minimal validation happening on the config after parsing. IIRC its mostly to catch issues that the libraries used do not like, like passing an empty DB url to the underlying stdlib DB adapters.

We should prob come up with some kind of 'rule' to figure out what we validate and what we dont

server/evaluator.go Show resolved Hide resolved
server/middleware.go Outdated Show resolved Hide resolved
server/middleware.go Show resolved Hide resolved
@markphelps markphelps merged commit 61d898b into main Jul 25, 2022
@markphelps markphelps deleted the redis branch July 25, 2022 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add redis as storage backend
3 participants