Skip to main content
Task debouncing is a mechanism used in Polar’s background worker system to prevent duplicate execution of tasks that are triggered multiple times in a short amount of time. This is particularly useful for operations that might be triggered by multiple events or user actions.

When to use task debouncing

Use task debouncing when you have background tasks that:
  • Might be triggered multiple times for the same logical operation
  • Have some tolerance for delayed execution
  • Would benefit from reduced database load and resource usage
Common use cases:
  • Updating customer metrics after multiple usage events
  • Processing webhook events that might arrive in quick succession
  • Any operation where you want to “batch” multiple triggers into a single execution

How to use task debouncing

To add debouncing to a Dramatiq task, you need to:
  1. Define a debounce key function: This function generates a unique key that identifies the logical operation being debounced. It takes the same arguments as your task and returns a string key.
  2. Configure the actor with debounce options: Set the debounce key function and thresholds.

Example implementation

Configuration options

  • debounce_key: A function that takes the same arguments as your task and returns a string key
  • debounce_min_threshold: Minimum delay (in seconds) before the task can execute. Defaults to WORKER_DEFAULT_DEBOUNCE_MIN_THRESHOLD if not set.
  • debounce_max_threshold: Maximum delay (in seconds) before the task must execute. Defaults to WORKER_DEFAULT_DEBOUNCE_MAX_THRESHOLD if not set.

Optional debouncing

If you want to make debouncing optional based on runtime conditions, you can set the debounce key function to return None when you don’t want to debounce. For example:

How it works

Architecture overview

The task debouncer consists of several components:
  1. Debounce key storage: Uses Redis to store debounce state with a 1-hour TTL
  2. Middleware: DebounceMiddleware that intercepts task processing
  3. Enqueue logic: set_debounce_key() function that sets up debounce state when tasks are enqueued
  4. Metrics: Tracks debounced tasks and execution delays

Debounce key structure

Debounce keys are stored in Redis as hash objects with this structure:

Execution flow

  1. Task enqueue: When a debounced task is enqueued:
    • The set_debounce_key() function creates/updates a Redis hash
    • The first enqueue sets the enqueue_timestamp
    • Subsequent enqueues update the message_id but preserve the original timestamp. They take the “ownership” of the task.
    • A minimum delay is applied to the task
  2. Task processing: When a worker picks up a debounced task:
    • DebounceMiddleware.before_process_message() checks the debounce state
    • If the key was already executed, the task is skipped
    • If the current message owns the key (message_id matches), it executes
    • If another message owns the key, it checks if max threshold is reached
    • If max threshold is reached, the current task executes and becomes the new owner
  3. Post-execution: After successful execution:
    • The executed flag is set to prevent further executions from tasks in the same debounce window
    • Metrics are recorded for monitoring

Debounce scenarios

The following scenarios illustrate how the debouncer behaves in common situations. In all examples, min_threshold is 10s and max_threshold is 60s.

Scenario 1: Single enqueue — basic execution

The simplest case: one task is enqueued, delayed by min_threshold, and executed.

Scenario 2: Multiple enqueues coalesce into one execution

Three tasks are enqueued for the same debounce key in quick succession. Only the last one (the owner) executes; the others are skipped.

Scenario 3: Max threshold prevents starvation

When tasks are continuously enqueued, ownership keeps shifting and the owner never gets a chance to run. The max threshold guarantees execution after a bounded delay.

Scenario 4: Re-enqueue after execution starts a new window

Once a task has executed and marked executed: 1, a new enqueue resets the debounce state and starts a fresh window.

Monitoring and metrics

The debouncer emits two key metrics:
  • polar_task_debounced_total: Counter of tasks that were skipped due to debouncing
  • polar_task_debounce_delay_seconds: Histogram of delays between first enqueue and execution
Both metrics are labeled with queue and task_name for filtering.