Status: ActiveCreated: 2026-06-17
Updated 2026-08-28
Abstract
The async workflow that we have today relies on worker services using Dramatiq to read from one or multiple specific queues. We specify up front how many workers, how many processes and how many threads each process have. This proposal is to migrate this workflow to instead use the equivalent priorities as SQS queues and a Lambda function per queue. This will allow us to scale the concurrency of each task worker independently of each other. It will allow us to automatically increase our capacity if we need to catch up on a queue we are lagging behind on, as well as allow us to easily split out work into new queues if we need to scale parts more independently. This will change our architecture and infrastructure needs, and move us one step closer to running on AWS.Motivation
Problems with Current Approach
The current approach to workers that we have works fine for stable workloads. It is very easy to add new tasks (just specify which queue they should be put on) and they will automatically get picked up. The main issues we have today is:- When we get overwhelmed with tasks on one of the queues the other workers will not pick up and help with the tasks on the queues they are not listening to.
- We have to pre-emptively decide which queue a task should go on (high prio, medium prio, etc). This makes it tricky to give more resources to a task that has overwhelmed a queue if we realise that it was more important than previously thought.
- When the queues are empty we have a bunch of service workers that are running without doing any work.
Desired Outcomes
We use async workers for many different things, but oftentimes we require the work to be conducted in fairly short succession from when it was scheduled. This proposal aims to ensure that we don’t get overloaded by a sudden influx of tasks, self created or otherwise. Additionally it should stay easy to reason about the code running and our infrastructure, as well as to know what code is running where.Proposal
The proposal is to move the worker workloads to AWS, and set up SQS with Lambda functions acting on the messages from SQS. During roll out we will have a list of which workers run in Lambda, and which run in Dramatiq. In the enqueue_job we will use this to determine whether to enqueue the task via Dramatiq onto Redis, or onto SQS.Retry exception handler in Dramatiq.
The AWS environments will have a single static external IP per environment (production, sandbox, test). These can be used with the IP allow listing on Render to allow the Lambdas to access the database.
Redis will on the other hand be segregated into Render and AWS instances. Since there are only a few touch points in Redis (Dramatiq, Customer state cache, Metrics cache), most of the usage is fine to be non-interlinked. The main drawbacks of this is that we will not be able to share cache between non-migrated tasks and migrated tasks, and we will not be able to enqueue non-migrated tasks from AWS. Both of these are deemed as acceptable trade-offs to prevent us from going above the connection limit on Redis on Render. The AWS Redis has higher connection limits which fits the higher concurrency limits of Lambdas better.
Potential problems and their solutions
- We can hit ENI limits
- Hard limit timeout of Lambdas is 15m
Local development
Local development will utilise localstack which has support for both SQS and Lambda, and will be encapsulated in thedev commands used by the engineering team.

