TaskTiger Documentation

repository·master·Indexed 23 days ago

https://github.com/closeio/tasktiger

A reliable and flexible Python task queue backed by Redis. TaskTiger supports advanced features including task locking, batching, hierarchical subqueues, and multiple worker execution models (fork or sync). It provides a Celery-compatible @task decorator, built-in retry mechanisms with custom backoffs, and tools for inspecting, requeueing, and purging tasks. The library includes a CLI for worker management and supports integration with Rollbar for error handling.

Tokens
3.9K
Snippets
12
Records
18
Agent score
31%

What's inside TaskTiger

  1. Core features of TaskTiger

    master

    TaskTiger provides several advanced queue management features:

    • Worker Execution Models: Supports per-task forking (to prevent memory leaks and enforce hard timeouts) or synchronous workers (for higher performance and connection reuse).
    • Unique Queues: Prevents duplicate tasks from being queued if an identical task is already waiting.
    • Task Locks: Prevents multiple instances of tasks with similar arguments from running simultaneously by acquiring a lock.
    • Retries: Supports retrying exceptions with fixed, linear, exponential, or custom intervals.
    • Flexible & Subqueues: Supports hierarchical queues (e.g., process_emails.CUSTOMER_ID). Workers can process specific queues or all subqueues to ensure fair resource distribution.
    • Batch Queues: Combines multiple tasks into a single execution to improve throughput.
    • Scheduled/Periodic Tasks: Allows tasks to run at a specific time or on a recurring interval.
    • Reliability: Uses atomic state moves and re-executes tasks if a worker crashes after a timeout.
    • Error Handling: Unsuccessful tasks that are not configured for retry are moved to an error queue for manual inspection.
  2. Pause queue processing using system locks

    master
    To prevent workers from processing a specific queue for a period of time, you can place a system lock on it using tiger.set_queue_system_lock(). This is often used in conjunction with the --max-workers-per-queue option.
  3. Quick start with TaskTiger

    master

    To get started with TaskTiger, follow these three steps:

    1. Define your tasks: Create a Python file (e.g., tasks.py) containing the functions you want to run asynchronously.
    2. Queue the task: Use a TaskTiger instance and its .delay() method to add the task to the Redis queue.
    3. Run a worker: Execute the tasktiger command in your terminal. Ensure your PYTHONPATH includes the directory containing your task definitions so the worker can import them.

    Note: TaskTiger uses Redis as its backend.

    # tasks.py
    def my_task():
        print('Hello')
    
    # In your application code:
    import tasktiger, tasks
    tiger = tasktiger.TaskTiger()
    tiger.delay(tasks.my_task)
    # Run the worker
    % PYTHONPATH=. tasktiger
  4. Integrate Rollbar for error handling

    master

    TaskTiger supports Rollbar integration to log task errors. To enable this, initialize Rollbar and add a StructlogRollbarHandler (from tasktiger.rollbar) to the TaskTiger logger. The handler accepts a prefix string for all reported messages.

    import logging
    import rollbar
    import sys
    from tasktiger import TaskTiger
    from tasktiger.rollbar import StructlogRollbarHandler
    
    tiger = TaskTiger(setup_structlog=True)
    
    rollbar.init(ROLLBAR_API_KEY, APPLICATION_ENVIRONMENT, 
                 allow_logging_basic_config=False)
    rollbar_handler = StructlogRollbarHandler('TaskTiger')
    rollbar_handler.setLevel(logging.ERROR)
    tiger.log.addHandler(rollbar_handler)
    
    tiger.run_worker_with_args(sys.argv[1:])
  5. Configure Task Options via Decorators or delay()

    master

    TaskTiger allows you to specify task behavior using either a @tiger.task decorator or the tiger.delay() method.

    • Task Decorator: Used to define default behavior for a task. Options set here are the defaults for that task type.
    • tiger.delay(): Used when queueing a task. Options passed to delay() will override any settings defined in the task decorator.

    Important: When using delay(), the task must be defined in a module other than the one being executed (it cannot be in the __main__ module).

    @tiger.task(queue='myqueue', unique=True)
    def my_task():
        print('Hello')
    
    # The task will be queued in "otherqueue", even though the task decorator
    # says "myqueue".
    tiger.delay(my_task, queue='otherqueue')
  6. Configure TaskTiger via the TaskTiger constructor

    master

    The tasktiger.TaskTiger constructor is used to initialize the queue manager and configure its behavior.

    Arguments

    • connection: A Redis connection object. Important: The connection must be initialized with decode_responses=True to prevent encoding issues in Python 3.
    • config: A dictionary of configuration options.
    • setup_structlog: If set to True, TaskTiger will automatically set up structured logging using structlog.

    Common Configuration Options

    • ALWAYS_EAGER: If True, all tasks (except those scheduled for the future via when) execute locally and synchronously. This is ideal for testing.
    • BATCH_QUEUES: A dictionary mapping queue names to batch sizes. For example, {'my_queue': 50} allows the worker to pull up to 50 tasks at once. Tasks in these queues must be declared with batch=True. Subqueues inherit these settings unless specifically overridden.
    • ONLY_QUEUES: A list of queue names. If provided, workers will only process these specific queues (and their subqueues) unless overridden via CLI flags.
    import tasktiger
    from redis import Redis
    
    conn = Redis(db=1, decode_responses=True)
    tiger = tasktiger.TaskTiger(connection=conn, config={
        'BATCH_QUEUES': {
            'my_batch_queue': 50,
            'my_batch_queue.send_email': 10,
        },
    })
  7. Run TaskTiger using Docker Compose

    master

    You can deploy TaskTiger and its required Redis backend using Docker Compose. The setup defines two services:

    1. redis: Uses the redis:7.0.9 image and exposes port 6379.
    2. tasktiger: Builds from the local Dockerfile and connects to the Redis service.

    To connect the TaskTiger service to Redis, the REDIS_HOST environment variable is set to redis.

    version: "3.7"
    services:
      redis:
        image: redis:7.0.9
        expose:
          - 6379
      tasktiger:
        build:
          context: .
          dockerfile: Dockerfile
        environment:
          REDIS_HOST: redis
        volumes:
          - .:/src
        depends_on:
          - redis
  8. Purge errored tasks periodically

    master

    To prevent the error queue from growing indefinitely in Redis, use the purge_errored_tasks method. This can be automated by creating a periodic task using the periodic helper.

    from tasktiger import TaskTiger, periodic
    import datetime
    
    tiger = TaskTiger()
    
    @tiger.task(schedule=periodic(hours=1))
    def purge_errored_tasks():
        tiger.purge_errored_tasks(
            limit=1000,
            last_execution_before=(
                datetime.datetime.utcnow() - datetime.timedelta(weeks=12)
            )
        )
  9. Create a custom TaskTiger launch script

    master

    If you need to set up a specific environment (e.g., via a manage.py script) before launching workers, use the run_worker_with_args method. This allows you to pass command-line arguments through your script to the TaskTiger worker.

    import sys
    from tasktiger import TaskTiger
    
    try:
        command = sys.argv[1]
    except IndexError:
        command = None
    
    if command == 'tasktiger':
        tiger = TaskTiger(setup_structlog=True)
        # Strip the "tasktiger" arg when running via manage, so we can run e.g. 
        # ./manage.py tasktiger --help
        tiger.run_worker_with_args(sys.argv[2:])
        sys.exit(0)
  10. Use the @task decorator for Celery-compatible syntax

    master

    While simple functions can be queued using tiger.delay(func), you can use the @tiger.task() decorator to enable a more concise, Celery-compatible syntax.

    When a task is decorated, you can call .delay() directly on the function object itself rather than through the TaskTiger instance.

    # tasks.py
    import tasktiger
    tiger = tasktiger.TaskTiger()
    
    @tiger.task()
    def my_task(name, n=None):
        print('Hello', name)
    
    # Queuing the task:
    # Option 1: Standard syntax
    tiger.delay(my_task, args=('John',), kwargs={'n': 1})
    
    # Option 2: Celery-compatible syntax (requires @tiger.task())
    my_task.delay('John', n=1)
  11. Implement Custom Retries with RetryException

    master

    If standard retry options are insufficient, you can raise a RetryException from within your task function to implement complex logic (e.g., different backoffs for different error types).

    Key RetryException arguments:

    • method: The retry method to use for this specific exception.
    • original_traceback: Boolean (default False). If True, the original traceback where the exception was caught is logged.
    • log_error: Boolean (default True). If False, a warning is logged instead of an error when the task fails permanently.
    from tasktiger.exceptions import RetryException
    from tasktiger.retry import exponential, fixed
    
    def my_task():
        if not ready():
            # Retry every minute up to 3 times
            raise RetryException(method=fixed(60, 3))
    
        try:
            some_code()
        except NetworkException:
            # Exponential backoff, log original traceback, don't log error on final failure
            raise RetryException(method=exponential(60, 2, 5),
                                   original_traceback=True,
                                   log_error=False)
  12. Access current task context within a task function

    master

    You can access information about the task currently being executed from within the task function itself:

    • Non-batch tasks: Use tiger.current_task to get the current Task instance.
    • Batch tasks: Use tiger.current_tasks to get a list of all Task instances currently being processed in that batch (in the same order they were passed).
    from tasktiger import TaskTiger
    
    tiger = TaskTiger()
    
    @tiger.task(batch=True)
    def my_task(args):
        for task in tiger.current_tasks:
            print(task.n_executions())