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.

Easy Python 3.9+ Aug 9, 2026 Reliability & rate limiting 13 views 0 copies

Python code

34 lines
Python 3.9+
import 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

stdout
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

  1. Use `time.monotonic()` instead of `time.time()` to avoid wall-clock jumps
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from Reliability & rate limiting

Related tutorials and quizzes for this topic.