-
Notifications
You must be signed in to change notification settings - Fork 435
feat: add permanent environment document cache #5187
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
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
2 Skipped Deployments
|
Uffizzi Preview |
api/environments/models.py
Outdated
if is_saas(): | ||
environment_wrapper.write_environments(environments) | ||
|
||
if ( | ||
project.edge_v2_environments_migrated | ||
and environment_v2_wrapper.is_enabled | ||
): # type: ignore[union-attr] | ||
environment_v2_wrapper.write_environments(environments) | ||
elif not settings.EXPIRE_ENVIRONMENT_DOCUMENT_CACHE: | ||
environment_document_cache.set_many( | ||
{ | ||
e.api_key: map_environment_to_environment_document(e) | ||
for e in environments | ||
} | ||
) |
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.
I don't believe the is_saas()
check belongs here. I'd rather allow our users to employ their Dynamo in the future, e.g., by writing a Dynamo cache backend.
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.
Hmm... I somewhat agree with this. Let me see if I can refactor this (and the environment variables as per the above 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.
Looking into this a bit more, I think my concern here is that this implies a much larger refactor.
I guess the ideal solution would be to refit our SaaS platform to essentially have something like:
ENVIRONMENT_DOCUMENT_CACHE_MODE: PERSISTENT
ENVIRONMENT_DOCUMENT_CACHE_BACKEND: cache.backends.custom.dynamodb
where cache.backends.custom.dynamodb
is our own implementation of the django cache backend which handles the interactions with dynamodb.
While the prospect of this certainly excites me, I don't think it should necessarily be in scope for this piece of work.
An option to solve your direct comment here would be to remove the call to is_saas()
and instead use some other setting, perhaps derived from whether e.g. ENVIRONMENTS_TABLE_NAME_DYNAMO
is set.
Thoughts @khvn26?
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.
I actually found a half decent halfway solution as there was already a conditional check in the code that was only relevant to the dynamodb path, so I've just used that as the check instead of is_saas
for now. I still think the custom cache backend would be the best option, and may well simplify a lot of the dynamo integration code.
Codecov ReportAll modified and coverable lines are covered by tests ✅
Additional details and impacted files@@ Coverage Diff @@
## main #5187 +/- ##
========================================
Coverage 97.53% 97.54%
========================================
Files 1225 1227 +2
Lines 42551 42676 +125
========================================
+ Hits 41503 41629 +126
+ Misses 1048 1047 -1 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
More testing is needed here, but initial signs are good regarding performance. I tested this code by building the docker image locally and using the following (simplified for brevity) docker compose file. volumes:
pgdata:
services:
postgres:
image: postgres:15.5-alpine
environment:
POSTGRES_PASSWORD: password
POSTGRES_DB: flagsmith
container_name: flagsmith_postgres
volumes:
- pgdata:/var/lib/postgresql/data
healthcheck:
test: ['CMD-SHELL', 'pg_isready -d flagsmith -U postgres']
interval: 2s
timeout: 2s
retries: 20
start_period: 20s
flagsmith:
image: flagsmith/flagsmith:local
environment:
DATABASE_URL: postgresql://postgres:password@postgres:5432/flagsmith
ENVIRONMENT: production # set to 'production' in production.
DJANGO_ALLOWED_HOSTS: '*' # Change this in production
ALLOW_ADMIN_INITIATION_VIA_CLI: 'true' # Change this in production
FLAGSMITH_DOMAIN: localhost:8000 # Change this in production
DJANGO_SECRET_KEY: secret
# Enable Task Processor
TASK_RUN_METHOD: TASK_PROCESSOR
ENVIRONMENT_DOCUMENT_CACHE_MODE: PERSISTENT
ENVIRONMENT_DOCUMENT_CACHE_BACKEND: django.core.cache.backends.redis.RedisCache
ENVIRONMENT_DOCUMENT_CACHE_LOCATION: redis://redis_db:6379/1
ports:
- 8000:8000
healthcheck:
test: ['CMD-SHELL', 'python /app/scripts/healthcheck.py']
interval: 2s
timeout: 2s
retries: 20
start_period: 20s
depends_on:
postgres:
condition: service_healthy
redis_db:
condition: service_started
flagsmith_processor:
image: flagsmith/flagsmith:local
environment:
DATABASE_URL: postgresql://postgres:password@postgres:5432/flagsmith
USE_POSTGRES_FOR_ANALYTICS: 'true'
LOG_LEVEL: DEBUG
ENVIRONMENT_DOCUMENT_CACHE_MODE: PERSISTENT
ENVIRONMENT_DOCUMENT_CACHE_BACKEND: django.core.cache.backends.redis.RedisCache
ENVIRONMENT_DOCUMENT_CACHE_LOCATION: redis://redis_db:6379/1
depends_on:
flagsmith:
condition: service_healthy
command: run-task-processor
redis_db:
image: redis:latest In order to compare performance, I simply removed the Here are the before and afters: Before
After
|
62b7632
to
7e9de60
Compare
api/environments/models.py
Outdated
flagsmith_environment_document_cache_result.labels( | ||
api_key=api_key, | ||
result=CACHE_HIT if cache_hit else CACHE_MISS, | ||
).inc() |
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.
There's an argument to say that this metric incrementing should be done in Environment.get_environment_document
above but I decided that it didn't really make sense to track cache hits / misses in an instance that isn't configured to use the cache.
Changes
This PR sets out to add a permanent cache of the environment document, similar to how our SaaS environment works where it writes the document to dynamodb. This updates the current logic for caching the environment document, but hooks it into the same logic that updates the document in dynamodb for our SaaS platform. It also makes the cache backend customisable to allow self hosted installs to use, e.g. redis to cache the document.
This PR also adds a new metric called
flagsmith_environment_document_cache_result
which tracks the number of cache hits and misses for the environment document cache.How did you test this code?
Added a new test to verify the flow end-to-end. I also tested it manually, as can be seen in this comment.