Pattern: uWSGI-style reload-on-RSS for gunicorn+UvicornWorker via a pure-ASGI middleware that reads /proc/self/statm every N completed requests and SIGTERM-to-self over a threshold (uvicorn's SIGTERM handler drains gracefully; the arbiter respawns; the shared listen socket keeps siblings serving).
Trap 1: jittering the per-worker RSS threshold (e.g. 550MB +/- 30) looks like it staggers recycles, but under sustained load every worker's RSS climbs at the same rate, so all workers cross their thresholds within seconds of each other anyway. Observed live: 2 of 3 workers drained 5s apart, the instance lost most capacity, the platform health-check restarted it - the recycler amplified the outage it existed to prevent. Fix: an instance-wide cooldown stamp (a file in /dev/shm, shared by forked workers; check mtime before recycling) bounding recycles to one per ~120s. Memory creep is slow; recycling can wait.
Trap 2: under gunicorn --preload the middleware is constructed once in the master and forked, so init-time jitter (and the forked random state) is IDENTICAL across workers. Draw the jitter lazily on first check with random.SystemRandom (reads urandom per process).
Trap 3: never use ru_maxrss as the signal - it is a high-water mark and never decreases, so one spike condemns the worker to recycle forever. Read live RSS from /proc/self/statm (field 2 x pagesize).
Trap 4: during uvicorn's graceful drain the worker stops ticking gunicorn's arbiter heartbeat; cap the uvicorn graceful-shutdown timeout (Config kwarg named timeout_graceful_ + shutdown) below gunicorn --timeout, or a slow drain ends in arbiter SIGKILL. Under gunicorn's UvicornWorker set it by subclassing and extending CONFIG_KWARGS.
Meta-lesson: the jitter defect was invisible to reasoning and code review; it fell out immediately when the fix was load-tested on staging before production. Exercise production fixes on stage; per-worker randomness is not coordination.