better-queue

repository·master·Indexed 19 days ago

https://github.com/diamondio/better-queue

A flow control library for Node.js providing advanced queuing capabilities including persistence, batching, prioritization, and fine-grained timing controls. It supports concurrent processing, task merging, retries, and built-in SQL stores for SQLite and PostgreSQL.

Tokens
6.3K
Snippets
21
Records
23
Agent score
19%

What's inside better-queue

  1. Manage task IDs and merging

    master

    Tasks can be identified by an ID. By default, the queue looks for a task.id property. You can customize this behavior using the id option.

    Merging Tasks: If tasks share the same ID, they can be merged using a merge function. This is useful for aggregating data (e.g., counters) before processing.

    Replacing Tasks: By default, if tasks have the same ID, the new task replaces the previous one in the queue.

    // Customizing ID lookup
    var q = new Queue(fn, {
      id: 'name', // use task.name instead of task.id
      // OR
      id: function (task, cb) {
        cb(null, 'computed_id');
      }
    });
    
    // Merging tasks with the same ID
    var counter = new Queue(function (task, cb) {
      console.log("I have %d %ss.", task.count, task.id);
      cb();
    }, {
      merge: function (oldTask, newTask, cb) {
        oldTask.count += newTask.count;
        cb(null, oldTask);
      }
    });
    
    counter.push({ id: 'apple', count: 2 });
    counter.push({ id: 'apple', count: 1 });
    // Result: I have 3 apples.
  2. Quick start with Better Queue

    master

    Initialize a new queue by passing a processing function to the Queue constructor. The function receives the task input and a callback cb to signal completion. Use q.push(data) to add tasks to the queue.

    var Queue = require('better-queue');
    
    var q = new Queue(function (input, cb) {
      // Some processing here ...
      cb(null, result);
    })
    
    q.push(1)
    q.push({ x: 1 })
  3. Install TypeScript type definitions for Better Queue

    master

    To use Better Queue in a TypeScript project, install the type definitions from Definitely Typed:

    npm install --save @types/better-queue
    import Queue = require('better-queue')
    
    const q: Queue = new Queue(() => {});
  4. Use Better Queue in the browser with Webpack

    master

    To use Better Queue in a browser environment via Webpack, you must explicitly provide a store (the default in-memory store is not automatically provided). It is recommended to use better-queue-memory.

    import Queue = require('better-queue')
    import MemoryStore = require('better-queue-memory')
    
    var q = new Queue(function (input, cb) {
      // ... processing ...
      cb(null, result);
    }, {
        store: new MemoryStore(),
      });
  5. Configure retries and timeouts

    master

    Manage task failures and execution limits using these options:

    • maxRetries: Number of times a failed task should be retried.
    • retryDelay: Milliseconds to wait before retrying a failed task.
    • maxTimeout: Maximum time (ms) allowed for a task to complete before it is aborted with an error.
    // Retry 10 times with a 1s delay
    var q = new Queue(fn, { maxRetries: 10, retryDelay: 1000 });
    
    // Abort task if it takes longer than 2 seconds
    var q = new Queue(fn, { maxTimeout: 2000 });
  6. Configure built-in SQL stores (SQLite and PostgreSQL)

    master

    Better Queue supports persistent storage via SQL. You can configure these by passing a store object in the Queue options.

    SQLite

    Requires npm install sqlite3 and better-queue-sql or better-queue-sqlite.

    PostgreSQL

    Requires npm install pg.

    Note: Ensure the dialect and connection parameters (host, port, etc.) are correctly provided.

    // SQLite
    var q = new Queue(fn, {
      store: {
        type: 'sql',
        dialect: 'sqlite',
        path: '/path/to/sqlite/file'
      }
    });
    
    // PostgreSQL
    var q = new Queue(fn, {
      store: {
        type: 'sql',
        dialect: 'postgres',
        host: 'localhost',
        port: 5432,
        username: 'username',
        password: 'password',
        dbname: 'template1',
        tableName: 'tasks'
      }
    });
  7. Configure queue concurrency and order

    master

    You can control how many tasks run simultaneously and the order in which they are processed using the options object in the Queue constructor:

    • concurrent: The number of tasks to process at the same time (default is 1).
    • filo: Set to true to turn the queue into a stack (First-In-Last-Out).
    // Run 3 tasks at a time
    var q = new Queue(fn, { concurrent: 3 });
    
    // Process items in FILO order
    var q = new Queue(fn, { filo: true });
  8. Control queue timing and delays

    master

    Fine-tune when tasks are processed using timing options:

    • batchDelay: Wait X ms before processing a batch (useful for 'timed cargo').
    • batchDelayTimeout: Force processing a batch if no new tasks have been added for X ms.
    • afterProcessDelay: Delay between the completion of one task and the start of the next.
    • precondition: A function that must return true before the queue processes the next batch. If it returns false, the queue waits and retries based on preconditionRetryTimeout.
    // Batch processing with delays
    var q = new Queue(fn, {
      batchSize: 50,
      batchDelay: 5000,
      batchDelayTimeout: 1000
    });
    
    // Using preconditions (e.g., checking internet connectivity)
    var q = new Queue(fn, {
      precondition: function (cb) {
        isOnline(function (err, ok) {
          cb(null, ok);
        });
      },
      preconditionRetryTimeout: 10000
    });
  9. Filter, validate, and prioritize tasks

    master

    Use the filter and priority options to control how tasks enter and move through the queue:

    • filter: A function that can transform input or reject it (by calling the callback with an error). Useful for validation or pre-processing.
    • priority: A function that assigns a numeric priority to a task. Higher numbers are processed first.
    // Filtering and transforming input
    var greeter = new Queue(function (name, cb) {
      console.log("Hello, %s!", name);
      cb();
    }, {
      filter: function (input, cb) {
        if (input === 'Bob') return cb('not_allowed');
        return cb(null, input.toUpperCase());
      }
    });
    
    // Prioritizing tasks
    var q = new Queue(fn, {
      priority: function (name, cb) {
        if (name === "Steve") return cb(null, 10);
        cb(null, 1);
      }
    });
  10. Implement a custom storage engine

    master

    To use a custom store, implement a set of required functions and either pass the store to the Queue constructor or use the queue.use(store) method.

    Required Store Interface:

    • connect(cb): Connect to your storage backend.
    • getRunningTasks(cb): Returns a map of running tasks (lockId => taskIds).
    • getTask(taskId, cb): Retrieves a specific task.
    • putTask(taskId, task, priority, cb): Saves a task with a given priority.
    • takeFirstN(n, cb): Removes the first n items (sorted by priority and age).
    • takeLastN(n, cb): Removes the last n items (sorted by priority and recency).
    var q = new Queue(fn, { store: myStore });
    // OR
    q.use(myStore);
  11. Implement batch processing

    master

    To process multiple tasks at once, set the batchSize option. The processing function will then receive an array (the batch) instead of a single task.

    var ages = new Queue(function (batch, cb) {
      // batch is an array of tasks
      // e.g., [ { id: 'steve', age: 21 }, { id: 'john', age: 34 } ]
      cb();
    }, { batchSize: 3 });
    
    ages.push({ id: 'steve', age: 21 });
    ages.push({ id: 'john', age: 34 });
    ages.push({ id: 'joe', age: 18 });
    ages.push({ id: 'mary', age: 23 });