How to Implement Message Visibility Timeout Renewal in Python
Simulate queue message visibility control with timeout renewal using a simple Python class that tracks received time and visibility state.
Python code
34 linesimport time
import uuid
class Message:
def __init__(self, body, visibility_timeout=30):
self.body = body
self.visibility_timeout = visibility_timeout
self.receipt_handle = str(uuid.uuid4())
self.received_at = time.time()
self.deleted = False
def is_visible(self):
return self.deleted or time.time() >= self.received_at + self.visibility_timeout
def renew_timeout(self, new_timeout):
self.visibility_timeout = new_timeout
self.received_at = time.time()
return self.receipt_handle
if __name__ == "__main__":
msg = Message("Hello, world!", visibility_timeout=2)
print(f"Initial receipt handle: {msg.receipt_handle}")
print(f"Visible initially: {msg.is_visible()}")
time.sleep(1)
print(f"Visible after 1s: {msg.is_visible()}")
msg.renew_timeout(3)
print(f"Renewed timeout to {msg.visibility_timeout}s")
print(f"Visible after renewal: {msg.is_visible()}")
time.sleep(2)
print(f"Visible 2s after renewal: {msg.is_visible()}")
Output
Initial receipt handle: 3f4e9a2b-1c8d-4e5f-9a0b-6c7d8e9f0a1b
Visible initially: False
Visible after 1s: False
Renewed timeout to 3s
Visible after renewal: False
Visible 2s after renewal: False
How it works
The Message class models a queue message by storing received_at as the epoch time when it was first received. The is_visible() method compares the current time against received_at plus the visibility_timeout to determine whether a consumer can process it. Renewing the timeout resets received_at to now with a new timeout, effectively extending the invisibility window. The deleted flag allows a message to stay visible (or marked done) once processed. This pattern mirrors how SQS visibility timeouts protect in-flight messages from being redelivered.
Common mistakes
- Forgetting to reset `received_at` when renewing the timeout, causing no actual extension
- Mixing up the visibility semantics — visible means available to new consumers, not deleted
- Using a fixed timeout without checking for renewed values in long-running workers
- Ignoring that `deleted` messages remain visible, which can double-count completed work
Variations
- Use `time.monotonic()` instead of `time.time()` to avoid wall-clock jumps
- Store the timeout deadline in a single timestamp instead of recomputing from `received_at`
Real-world use cases
- Managing SQS message redelivery semantics while your worker processes a long batch job.
- Simulating retry logic for payments where each attempt must wait for the previous to settle.
- Testing consumer competition by controlling how long a lock is held before another worker retries.
Sponsored
More from Reliability & rate limiting
- At Least Once with Idempotent Consumer in Python medium
- Build a Rate Limiter Decorator in Python easy
- Build a queue-based admission control system in Python easy
- Chaos Inject Random Failures in Python easy
- Circuit breaker failure threshold count in Python medium
- Exactly Once Processing Dedupe Mock in Python easy
Keep learning
Related tutorials and quizzes for this topic.