Repository navigation
Performance & Helm Chart Improvements - #889
Draft
EvanDietzMorris wants to merge 42 commits into
Draft
EvanDietzMorris wants to merge 42 commits into
EvanDietzMorris wants to merge 42 commits into
Conversation
- separate retry functionality for errors from retries due to contention (failure to acquire a token or a db lock) - allow different max retry settings for each - make the backoff timing for normal retries constant/faster vs the exponential backoff for real errors - fix silent handling of merge lock retry exhaustion
The callback view used to json.loads the whole body, run pre_merge_process and validation, re-serialize it to save, then decompress and re-serialize it once more via mesg.to_dict() for a response the shepherd never reads. For large responses (44MB seen in prod) that expanded the message ~10x inside a gunicorn worker and got the worker OOM-killed mid-request. The view now stores the raw body compressed (Message.save_compressed_bytes), enqueues ingest_ara_response on commit, and returns a small ack. The task owns parse, result count, ara_response_complete, pre_merge_process, validation and the merge enqueue. Behavior changes: ara_response_complete fires from the task (slightly later); validation failure surfaces as child E/422 + ara_failed_validation instead of a synchronous 422; malformed JSON is marked E/500 by the task instead of a synchronous 500; unknown pk now returns 404 instead of 500.
EvanDietzMorris
marked this pull request as draft
September 11, 2026 19:50
Previously ARS alterations to messages were not saved to the database due to this original_data handling. That was bug-like behavior but it didn't matter because the altered messages were passed through the rabbit mq and used from there. Now that we read messages from the database we need to save them correctly.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Performance improvements + new Helm chart
This PR moves heavy processing out of the web server and makes Celery tasks lighter and more predictable. It simplifies how concurrency is controlled, which lets us remove Redis entirely. It also adds a Helm chart that deploys the whole ARS stack on Kubernetes.
1. ARA responses are processed in Celery, not in the web request
Before: when an ARA posted results back, the web server did all the work before responding. It parsed the full message, ran pre-merge processing and validation, compressed it, and then sent the whole message back in the response. ARA responses can be tens of MB, so a single large one could tie up a web worker for a long time. Many kinds of errors can also occur during that process, causing the ARA to see a dropped connection and retried, which added even more load.
Now: the endpoint stores the raw response (compressed, never parsed), queues a new
ingest_ara_responseCelery task, and returns a small acknowledgement right away. The celery task then does the processing the web server used to do.Why it matters:
What ARAs and subscribers will notice:
ara_failed_validationnotification is sent.ara_response_completeis now sent by the worker, so it arrives slightly later.1a. New
received_attimestampThe callback endpoint now also records the moment an ARA's response arrives, before storing it, so that:
catch_timeoutno longer applies the 5 minute limit to messages that already have a response. Those get 20 minutes from received_at to finish processing instead, so a lost or crashed ingest task can't leave the query running forever. If the ingest task runs after its message has already timed out, it does nothing, so a timed-out message can't flip back to Done and get merged.2. Tasks pass IDs instead of whole messages
Before: the merge task received the entire message as its argument. That means the full message went through RabbitMQ, and went through again every time the task retried.
Now: the task receives only IDs and loads the message from the database itself. Tasks are queued only after the database write commits, so a worker always finds the data it needs.
Why it matters:
3. Clearer merge retry behavior
Building off of PR #860.
Before: two very different situations shared one small retry limit. One is waiting for another merge on the same query to finish, which is normal. The other is a real error. When the limit ran out, the merge gave up silently.
Now: each situation has its own limit, and both are configurable:
ARS_MERGE_LOCK_MAX_RETRIES, default 100).ARS_MERGE_ERROR_MAX_RETRIES, default 8).If a merge gives up waiting, subscribers get a
merged_version_failednotification.4. Worker queues replace the Redis token gate, and Redis is removed
Before: a custom Redis-based token system limited how many expensive merges could run at once. It added a lot of moving parts. It only covered merges, not other heavy work, and in practice did not function well as intended. It could also cause messages to be sent through the rabbitMQ many more times than needed.
Now: heavy tasks (merging and ingesting ARA responses) go to a dedicated
heavyCelery queue. Everything else stays on the default queue. The number of heavy workers sets the limit on concurrent heavy work, and quick tasks like notifications never wait behind a merge.Why it matters:
The token gate was the only thing using Redis. The websocket/Channels setup that also referenced Redis was never enabled. So this PR removes Redis entirely: the gate code, the Channels settings and stub files, the Redis service in docker-compose, and the related dependencies.
5. Only the shepherd ARAs are active
Includes PR #880 - All non-shepherd ARAs and KPs are now listed as inactive in
config/config.yaml. This PR also fixes a bug where that list was ignored for actors that already existed in the database.6. Less tracing noise
Routine status checks and the health ping task are no longer traced.
7. New Helm chart (
helm/ars/)Before: the
deploy/chart runs the web server, Celery worker, RabbitMQ and Redis as containers in a single pod. That design has several problems:Now:
helm/ars/deploys the full stack, with each component in its own pod:The chart comes with two environment overlays:
renci-values.yaml: RENCI Sterling, with nginx ingress, TLS and sizing for the heavy workers and database.ncats-values.yaml: NCATS / transltr.io, with ECR images, ALB ingress, external RDS and node pinning.See
helm/ars/README.mdfor setup.The legacy
deploy/chart is left in place.Deployment notes
heavyqueue, or merges will stall. The Helm chart handles this. Single-worker setups (docker-compose, the legacy chart, CI) now run with-Q celery,heavy.ARS_MERGE_LOCK_MAX_RETRIESandARS_MERGE_ERROR_MAX_RETRIES. Removed env vars areARS_EXPENSIVE_*andREDIS_URL.Tests
New unit tests in
tr_sys/tests/unit/cover merge retries, ARA response ingestion and the callback endpoint. They need no database or broker. Run them fromtr_sys/withpython -m pytest tests/unit.