taskq

repository·v3·Indexed 23 days ago

https://github.com/vmihailenco/taskq

A Golang asynchronous task/job queue supporting multiple backends including Redis, SQS, IronMQ, and in-memory. It features automatic scaling, rate limiting, retries, and deduplication. The library provides tools for producing and consuming tasks, custom handler implementation via the Handler interface or NewHandler, and observability integration through an OpenTelemetryHook.

Tokens
5.9K
Snippets
11
Records
48
Agent score
79%

What's inside taskq

  1. Run the SQS backend example (Producer and Consumer)

    v3

    The SQS example is split into two separate processes: a consumer to process tasks and a producer to enqueue them. Run them in separate terminal sessions.

    1. Start the consumer first to ensure it is ready to receive tasks.
    2. Start the producer to send tasks to the SQS queue.
  2. Understand consumerConfig and its performance metrics

    v3

    The consumerConfig type is used to manage consumer settings, specifically the number of fetchers and workers, while tracking performance metrics through an embedded perfProfile.

    Key performance metrics available via the perfProfile methods are:

    • TPS(): Transactions Per Second (throughput).
    • ErrorRate(): The ratio of retries to total processed tasks.
    • Timing(): The duration of the last processing window.

    Note that consumerConfig also maintains a Score which is used by the internal configRoulette to determine the optimal configuration based on throughput, error rate, and timing.

  3. How TaskMap handles incoming messages

    v3

    The HandleMessage(msg *Message) method is the primary way to process an incoming message through the registry.

    1. It looks up the task by msg.TaskName.
    2. If the task is unknown, it returns an error and calculates a delay.
    3. If the task is found, it executes the task's handler.
    4. If the handler returns an error, HandleMessage calculates a retry delay based on the task's options or the error type (if the error implements the Delayer interface).
  4. Ensure a message is processed only once in a period

    v3

    TaskQ supports deduplication via the Name field. If multiple messages are added with the same Name, only one will be processed.

    Use OnceInPeriod to automatically generate a unique Name based on the provided arguments and a time period. This ensures that a specific task with specific arguments is only queued once per given duration.

    If no args are provided to OnceInPeriod, it uses the existing m.Args to generate the name.

    // Ensures the message is added only once every 10 minutes
    msg.OnceInPeriod(10 * time.Minute)
    
    // Or with specific args to define the uniqueness
    msg.OnceInPeriod(10 * time.Minute, "unique-key", 123)
  5. Consume tasks from a queue

    v3

    To start processing tasks from a queue, call the Start method on your queue instance. This method is blocking and will begin fetching and executing tasks based on the queue's configuration.

    // Start consuming the queue.
    if err := MainQueue.Start(context.Background()); err != nil {
        log.Fatal(err)
    }
  6. Produce tasks to a queue

    v3

    To add tasks to a queue, you must first create a Factory for your chosen backend (e.g., redisq.NewFactory()), register a Queue using RegisterQueue, and define tasks using RegisterTask. Once the queue is initialized, use the Add method on the queue instance to enqueue tasks. Tasks can be configured with arguments using the WithArgs method.

    import (
        "github.com/vmihailenco/taskq/v3"
        "github.com/vmihailenco/taskq/v3/redisq"
    )
    
    // Create a queue factory.
    var QueueFactory = redisq.NewFactory()
    
    // Create a queue.
    var MainQueue = QueueFactory.RegisterQueue(&taskq.QueueOptions{
        Name:  "api-worker",
        Redis: Redis, // go-redis client
    })
    
    // Register a task.
    var CountTask = taskq.RegisterTask(&taskq.TaskOptions{
        Name: "counter",
        Handler: func() error {
            IncrLocalCounter()
            return nil
        },
    })
    
    ctx := context.Background()
    
    // And start producing.
    for {
    	// Call the task without any args.
    	err := MainQueue.Add(CountTask.WithArgs(ctx))
    	if err != nil {
    		panic(err)
    	}
    	time.Sleep(time.Second)
    }
  7. Configure task processing with TaskOptions

    v3

    The TaskOptions struct defines how a specific task type is handled, retried, and recovered. When creating TaskOptions, you must provide a Name.

    Handler Signatures

    The Handler and FallbackHandler can be one of three function signatures:

    1. A zero-argument function: func()
    2. A function with arguments assignable from the message payload: func(arg1, arg2 Type)
    3. A function taking a single *Message argument: func(*Message)

    Note: Handlers may optionally take a context.Context as the first argument and may optionally return an error. If a handler returns a non-nil error, the message processing fails and will be retried according to the backoff settings.

    Retry and Backoff Settings

    • RetryLimit: Number of retries before the message is permanently failed and deleted. Defaults to 64.
    • MinBackoff: Minimum time between retries. Defaults to 30s.
    • MaxBackoff: Maximum time between retries. Defaults to 30m.
    • FallbackHandler: A function called after all retries have failed.
    • DeferFunc: An optional function used by the Consumer to recover from panics.
  8. Configure task queue with QueueOptions

    v3

    Use QueueOptions to configure the behavior of a task queue. You must provide a Name. The Init() method should be called to apply default values for workers, fetchers, timeouts, and storage.

    Key configuration behaviors:

    • Worker Scaling: MinNumWorker and MaxNumWorker control goroutine counts. If WorkerLimit is set, it overrides both to enforce a global concurrency limit.
    • Fetching: MaxNumFetcher controls how many goroutines fetch messages. ReservationSize determines how many messages a fetcher grabs at once.
    • Timeouts: ReservationTimeout is when a reserved message is returned to the queue. WaitTimeout is the long-polling duration.
    • Error Handling: PauseErrorsThreshold defines how many consecutive failures trigger a queue pause (set to -1 to disable or 0 for the default 100).
    • Rate Limiting: Supports RateLimit (using redis_rate.Limit) and an optional RateLimiter.
  9. Create messages for a task with WithArgs

    v3
    Once a *Task is registered, use its WithArgs method to construct a *Message ready to be enqueued. WithArgs takes a context.Context and a variadic list of arguments that match the signature of your registered Handler.