python
41 lines · 7 steps
Graceful shutdown in a queue worker
A worker loop that catches termination signals and exits only after the current task finishes.
Explained by
highlit
1import signal
2import time
3import logging
4
5logger = logging.getLogger("worker")
6
7
8class GracefulWorker:
9 def __init__(self, queue, poll_interval=1.0, visibility_timeout=30):
10 self.queue = queue
11 self.poll_interval = poll_interval
12 self.visibility_timeout = visibility_timeout
13 self._shutdown = False
14
15 def _request_shutdown(self, signum, frame):
16 logger.info("received signal %s, finishing current task then exiting", signum)
17 self._shutdown = True
18
19 def run(self):
20 signal.signal(signal.SIGTERM, self._request_shutdown)
21 signal.signal(signal.SIGINT, self._request_shutdown)
22
23 while not self._shutdown:
24 message = self.queue.receive(wait_seconds=self.poll_interval)
25 if message is None:
26 continue
27 self._process(message)
28
29 logger.info("worker stopped cleanly")
30
31 def _process(self, message):
32 try:
33 self.handle(message.body)
34 except Exception:
35 logger.exception("task %s failed, leaving for redelivery", message.id)
36 self.queue.release(message)
37 else:
38 self.queue.ack(message)
39
40 def handle(self, body):
41 raise NotImplementedError
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1A shared flag lets a signal handler request shutdown without interrupting work mid-task.
- 2Acknowledge messages only on success so failures are redelivered instead of silently lost.
- 3Leaving handle unimplemented turns the class into a reusable base for concrete workers.
Related explainers
rust
use serde::Deserialize; #[derive(Debug, Deserialize)] #[serde(untagged)]
Parsing flexible JSON shapes with serde
deserialization
enums
json
Intermediate
6 steps
ruby
require "shellwords" require "open3" module Backup
Building safe shell commands in Ruby
shell-out
subprocess
command-injection
Intermediate
7 steps
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
typescript
import { Injectable, effect, signal, computed } from '@angular/core'; interface Preferences { theme: 'light' | 'dark';
A signal-based preferences store in Angular
signals
state-management
persistence
Intermediate
7 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/graceful-shutdown-in-a-queue-worker-explained-python-928f/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.