Celery Migration — Platform Async Substrate
Replacing the platform's most fragile component — the thing every background process depended on — without the platform noticing.
Every piece of background work — indexing, exports, check-ins, scheduled jobs — ran through a legacy XMLRPC / scheduler jobserver that was a single point of fragility. When it crashed it left no logs. Jobs got stuck in the queue with no retry or cancellation. A lost database connection could take it down, and it had no concept of multi-tenant context, so a job had to be trusted to run against the right tenant by convention alone.
- 01
Designed and authored a managed Celery framework end-to-end — sole author across 42 files and roughly 10,800 lines — rather than bolting a queue onto the old jobserver.
- 02
Built multi-tenant context restoration into the task layer so every job reconstructs and runs inside the correct tenant context automatically, instead of relying on convention.
- 03
Made the workers self-healing: automatic reconnection to PostgreSQL on connection loss, and autorestart across the worker / search / AI containers so a transient fault never strands the queue.
- 04
Added a real task-lifecycle layer — scheduled-task logging, retry and cancellation — turning fire-and-forget jobs into ones you can observe, retry and stop.
- 05
Turned the old "crash with no logs" failure mode into a debuggable one by overhauling job-server logging first, which unblocked all the observability work that followed.
- 06
Dockerised the workers and hardened the serialization edges (a bytes/int leak and an exception-formatting mismatch surfacing on the long-tail error stream) so the system stayed quiet under real production load.
- 07
Ported 50+ legacy jobs onto the new substrate incrementally, so the migration shipped piece by piece rather than as one risky cutover.
The legacy jobserver was decommissioned and the platform's entire async workload moved onto the framework I designed. Two years on, I'm still its runtime owner — it's the reliability backbone every background process runs through.
- Celery
- Redis
- Python
- Docker
- PostgreSQL
- Sentry
Anchored across a cluster of shipped Redmine tickets: job-server logging (RM#7715), DB-reconnect robustness (RM#8829), Dockerisation (RM#8942), scheduling config (RM#8928), task-lifecycle management (RM#9180), file-handle exhaustion (RM#9002), and serialization fixes (RM#9098 / RM#9099).