agenda vs bee-queue vs bree vs bull vs kue vs node-resque
Node.js 向けバックグラウンドジョブキューライブラリの比較
agendabee-queuebreebullkuenode-resque類似パッケージ:

Node.js 向けバックグラウンドジョブキューライブラリの比較

これらのライブラリは、Node.js アプリケーションでメール送信、画像処理、データ集計などの重いタスクをバックグラウンドで非同期に実行するためのツールです。メインのリクエスト処理をブロックせずに済むため、ユーザー体験の向上やシステム全体の安定性に貢献します。Redis や MongoDB などの外部ストレージを利用してジョブを管理し、失敗時のリトライやスケジュール実行といった機能を提供します。

npmのダウンロードトレンド

3 年

GitHub Starsランキング

統計詳細

パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
agenda09,665297 kB1020日前MIT
bee-queue04,031107 kB486ヶ月前MIT
bree03,28892 kB293ヶ月前MIT
bull016,248309 kB1501年前MIT
kue09,444-2889年前MIT
node-resque01,411822 kB112日前Apache-2.0

Node.js バックグラウンドジョブ処理ライブラリの比較

Node.js でバックグラウンドタスクを処理する際、どのライブラリを選ぶかはシステムの信頼性に直結します。agendabee-queuebreebullkuenode-resque はすべてジョブキューを管理しますが、内部の仕組みや適した場面が異なります。実務で直面する課題に基づき、主要な違いを解説します。

💾 ストレージの仕組み:MongoDB vs Redis vs ファイル

ジョブの状態をどこに保存するかは、インフラ構成に大きな影響を与えます。

agenda は MongoDB を使用します。

  • データベースにジョブを保存するため、Redis を別途用意する必要がありません。
  • 既存の MongoDB クラスターを流用できるため、インフラコストを抑えられます。
// agenda: MongoDB 接続設定
const agenda = new Agenda({ db: { address: 'mongodb://localhost/agenda' } });
await agenda.start();

bee-queuebullkuenode-resque は Redis を使用します。

  • 高速なインメモリ処理が可能で、読み書きのレイテンシが小さくなります。
  • すでに Redis をキャッシュなどで使っている場合、追加の学習コストが低いです。
// bull: Redis 接続設定
const Queue = require('bull');
const queue = new Queue('my-queue', 'redis://127.0.0.1:6379');

bree はファイルシステムとプロセス管理を重視します。

  • 外部のキューサーバーに依存せず、ローカルのスクリプトを直接実行できます。
  • 設定ファイルでジョブを定義するため、管理がシンプルです。
// bree: 設定ファイルベース
const Bree = require('bree');
const bree = new Bree({
  root: './jobs',
  jobs: [{ name: 'daily-report', interval: 'daily' }]
});

📝 ジョブの定義と実行方法

ジョブをどのように登録し、実行するかは開発体験に直結します。

agenda は名前付きジョブを定義します。

  • 文字列でジョブ名を指定し、その名前で呼び出します。
  • スケジュールと実行ロジックが明確に分離されています。
// agenda: ジョブ定義と実行
agenda.define('send email', async (job) => {
  console.log(`Sending to ${job.data.to}`);
});
await agenda.schedule('in 5 minutes', 'send email', { to: 'user@example.com' });

bee-queue はキューインスタンスに対して処理を登録します。

  • キューごとに処理ロジックを紐付けるスタイルです。
  • コード量が少なく、直感的に理解しやすい構造です。
// bee-queue: 処理登録
queue.process(async (job) => {
  console.log(`Processing job ${job.id}`);
  return job.data;
});
const job = queue.createJob({ id: 123 });
await job.save();

bull もキューベースの処理登録を行います。

  • queue.process で処理を定義し、queue.add でジョブを投入します。
  • 戻り値や Promise の扱いが明確で、エラーハンドリングがしやすいです。
// bull: 処理登録と追加
queue.process(async (job) => {
  return doSomething(job.data);
});
await queue.add({ foo: 'bar' });

bree は独立したファイルとしてジョブを記述します。

  • ./jobs/daily-report.js のようなファイルを作成し、そこでロジックを書きます。
  • メインプロセスと分離されるため、メモリ管理が容易です。
// bree: ワーカースクリプト (jobs/daily-report.js)
module.exports = async () => {
  console.log('Running daily report');
};

kue はチェーンメソッドでジョブを作成します。

  • 古いスタイルの API で、ジョブ作成と保存を連続して行います。
  • 現在は非推奨ですが、既存コードではこの形式を見かけます。
// kue: ジョブ作成
const job = queue.create('email', { to: 'user' });
job.save((err) => {
  if (!err) console.log('Job saved');
});

node-resque はクラスベースでキューとワーカーを管理します。

  • Queue クラスで enqueue し、Worker クラスで処理します。
  • Resque プロトコルに準拠した明確な役割分担があります。
// node-resque: キューとワーカー
const { Worker, Queue } = require('node-resque');
const queue = new Queue({ connection: { host: 'localhost' } });
await queue.connect();
await queue.enqueue('queueName', 'jobName', ['arg1']);

🕒 スケジュールと定期実行

特定の時刻にジョブを実行したい場合、ライブラリの対応状況が重要です。

agenda はスケジュール機能が強力です。

  • scheduleevery メソッドで、人間が読める形式の時間を指定できます。
  • 複雑な cron 表現よりも直感的な記述が可能です。
// agenda: スケジュール実行
await agenda.every('10 minutes', 'calculate stats');
await agenda.schedule('tomorrow at noon', 'send report');

bree は cron 構文や間隔指定をサポートします。

  • 設定ファイルに intervalcron を書くだけで済みます。
  • 定期的なバッチ処理に特化しているため、設定が最も簡単です。
// bree: 間隔指定
const bree = new Bree({
  jobs: [
    { name: 'cleanup', interval: '5m' },
    { name: 'backup', cron: '0 0 * * *' }
  ]
});

bull はジョブ追加時にオプションで指定します。

  • repeat オプションを使って、ジョブの繰り返しを設定します。
  • キューの中にスケジュール情報が含まれるため、管理が一元化されます。
// bull: 繰り返しジョブ
await queue.add('send notification', {}, {
  repeat: { cron: '*/5 * * * *' }
});

bee-queuekuenode-resque は基本的なキュー機能に留まります。

  • 本体には高度なスケジューラーが含まれていないことが多いです。
  • 外部の cron ライブラリと組み合わせて使う必要があります。
// node-resque: 基本的な enqueue のみ
// スケジュール機能は本体にないため、外部で管理が必要
await queue.enqueue('high', 'sendEmail', ['user@example.com']);

⚠️ メンテナンス状況と将来性

ライブラリの保守状況は、長期的なプロジェクトの安定性を決めます。

kue は公式に非推奨です。

  • GitHub リポジトリはアーカイブされており、更新は停止しています。
  • セキュリティ脆弱性が見つかった場合でも修正されないため、新規採用は避けるべきです。
// kue: 使用を避けるべき
// const kue = require('kue'); // 非推奨

bull はメンテナンスモードに入っています。

  • 機能は安定していますが、新機能は後継の bullmq で開発されています。
  • 新規プロジェクトでは bullmq の検討を推奨しますが、bull もまだ広く使われています。
// bull: 後継に注意
// const Queue = require('bull'); // _legacy_
// const { Queue } = require('bullmq'); // _modern_

agendabee-queuebreenode-resque は継続してメンテナンスされています。

  • 定期的なアップデートがあり、Node.js の新バージョンにも対応しています。
  • コミュニティのサポートも活発で、問題発生時の対応が比較的容易です。
// bree: 活発なメンテナンス
const Bree = require('bree'); // 最新版が利用可能

🛡️ エラーハンドリングと再試行

ジョブが失敗したときにどう振る舞うかは、システムの信頼性に影響します。

agenda は再試行ロジックを内蔵しています。

  • 失敗したジョブを自動的に再スケジュールする設定が可能です。
  • 最大再試行回数や間隔を細かく制御できます。
// agenda: 再試行設定
agenda.define('task', { priority: 'high', concurrency: 10 }, async (job) => {
  // 失敗時は自動的にリトライ設定に従う
});

bull は詳細なアトンプト設定を持っています。

  • attempts オプションで再試行回数を指定できます。
  • 失敗時のバックオフ(待機時間)設定も可能です。
// bull: アトンプト設定
queue.add('video process', { id: 1 }, {
  attempts: 3,
  backoff: { type: 'fixed', delay: 2000 }
});

bee-queue はシンプルにエラーを返します。

  • 処理関数でエラーをスローすると、ジョブは失敗としてマークされます。
  • 複雑な再試行制御はアプリケーション側で実装する必要があります。
// bee-queue: エラー処理
queue.process(async (job) => {
  if (!job.data.valid) throw new Error('Invalid data');
});

bree はワーカープロセスの再起動で対応します。

  • プロセスがクラッシュしても、メインプロセスがそれを検知して再起動します。
  • ジョブ自体の再試行というよりは、実行環境の回復に近いです。
// bree: ワーカー管理
bree.on('worker error', (name, err) => {
  console.error(`Worker ${name} failed`, err);
  // 必要に応じて手動で再起動トリガー
});

💡 結論

agenda は MongoDB を使っているチームに最適です — データベース統合がスムーズで、スケジュール機能が豊富です。

bull は Redis 環境で高機能なキューが必要な場合に適していますが、bullmq への移行を視野に入れるべきです — 機能は豊富ですが、レガシーになりつつあります。

bree は cron ジョブや定期タスクに特化しています — 別プロセス実行による安定性が魅力で、設定も簡単です。

bee-queue はシンプルさを求める場合に選びます — Redis を使いつつ、設定を最小限に抑えたいプロジェクトに向いています。

kue は使用してはいけません — 非推奨であり、セキュリティリスクがあります。

node-resque は Resque エコシステムとの互換性が必要な場合に意味があります — 特定の環境以外では、他のモダンな選択肢を検討すべきです。

最終的には、既存のインフラ(Redis か MongoDB か)と、求められる機能(単純なキューか、複雑なスケジュールか)で決定します。

選び方: agenda vs bee-queue vs bree vs bull vs kue vs node-resque

  • agenda:

    MongoDB を既に使用しているプロジェクトで、ジョブの永続性と柔軟なスケジュール管理を重視する場合に選びます。データベースにジョブ状態を保存するため、Redis の導入が不要な点がメリットです。複雑な再試行ロジックや頻繁なスケジューリングが必要なシステムに適しています。

  • bee-queue:

    Redis を使ったシンプルで軽量なキュー構成が必要な場合に適しています。設定項目が少なく、コードが直感的であるため、小規模から中規模のタスク処理に最適です。高度な機能よりも、導入の手軽さと動作の確実性を優先するプロジェクトで選ばれます。

  • bree:

    cron ジョブや定期的なタスク実行をメインの目的とする場合に選びます。他のキューライブラリとは異なり、ワーカーを別プロセスとして実行するため、メモリリークのリスクを隔離できます。Node.js スクリプトを直接ジョブとして登録できるため、開発者の負担が少ないのが特徴です。

  • bull:

    Redis ベースで堅牢な機能セットが必要な場合に適していますが、後継の bullmq への移行も検討すべきです。ジョブの優先順位やイベント処理など、エンタープライズ向けの機能が充実しています。大規模な分散処理システムで実績が多いライブラリです。

  • kue:

    新規プロジェクトでは使用しないでください。このライブラリは公式に非推奨(Deprecated)となっており、メンテナンスが停止しています。セキュリティリスクやバグ修正が受けられないため、既存システムからの移行を強く推奨します。

  • node-resque:

    Ruby の Resque システムと互換性が必要な場合や、Resque プロトコルに基づく既存インフラと連携する場合に選びます。Redis をバックエンドに使用し、シンプルな FIFO キュー構成を提供します。特定のレガシーシステムとの統合が必要なシーンで価値を発揮します。

agenda のREADME

Agenda

Agenda

A light-weight job scheduling library for Node.js

NPM Version NPM Downloads

Migrating from v5? See the Migration Guide for all breaking changes.

Agenda 6.x

Agenda 6.x is a complete TypeScript rewrite with a focus on modularity and flexibility:

  • Pluggable storage backends - Choose from MongoDB, PostgreSQL, Redis, or implement your own. Each backend is a separate package - install only what you need.

  • Pluggable notification channels - Move beyond polling with real-time job notifications via Redis, PostgreSQL LISTEN/NOTIFY, or other pub/sub systems. Jobs get processed immediately when saved, not on the next poll cycle.

  • Modern stack - ESM-only, Node.js 18+, full TypeScript with strict typing.

See the 6.x Roadmap for details and progress.

Features

  • Minimal overhead job scheduling
  • Pluggable storage backends (MongoDB, PostgreSQL, Redis)
  • TypeScript support with full typing
  • Scheduling via cron or human-readable syntax
  • Configurable concurrency and locking
  • Real-time job notifications (optional)
  • Sandboxed worker execution via fork mode
  • TypeScript decorators for class-based job definitions

Installation

Install the core package and your preferred backend:

# For MongoDB
npm install agenda @agendajs/mongo-backend

# For PostgreSQL
npm install agenda @agendajs/postgres-backend

# For Redis
npm install agenda @agendajs/redis-backend

Requirements:

  • Node.js 18+
  • Database of your choice (MongoDB 4+, PostgreSQL, or Redis)

Quick Start

import { Agenda } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';

const agenda = new Agenda({
  backend: new MongoBackend({ address: 'mongodb://localhost/agenda' })
});

// Define a job
agenda.define('send email', async (job) => {
  const { to, subject } = job.attrs.data;
  await sendEmail(to, subject);
});

// Start processing
await agenda.start();

// Schedule jobs
await agenda.every('1 hour', 'send email', { to: 'user@example.com', subject: 'Hello' });
await agenda.schedule('in 5 minutes', 'send email', { to: 'admin@example.com', subject: 'Report' });
await agenda.now('send email', { to: 'support@example.com', subject: 'Urgent' });

Official Backend Packages

PackageBackendNotificationsInstall
@agendajs/mongo-backendMongoDBPolling onlynpm install @agendajs/mongo-backend
@agendajs/postgres-backendPostgreSQLLISTEN/NOTIFYnpm install @agendajs/postgres-backend
@agendajs/redis-backendRedisPub/Subnpm install @agendajs/redis-backend

Backend Capabilities

BackendStorageNotificationsNotes
MongoDB (MongoBackend)Storage only. Combine with external notification channel for real-time.
PostgreSQL (PostgresBackend)Full backend. Uses LISTEN/NOTIFY for notifications.
Redis (RedisBackend)Full backend. Uses Pub/Sub for notifications.
InMemoryNotificationChannelNotifications only. For single-process/testing.

Backend Configuration

MongoDB

import { Agenda } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';

// Via connection string
const agenda = new Agenda({
  backend: new MongoBackend({ address: 'mongodb://localhost/agenda' })
});

// Via existing MongoDB connection
const agenda = new Agenda({
  backend: new MongoBackend({ mongo: existingDb })
});

// With options
const agenda = new Agenda({
  backend: new MongoBackend({
    mongo: db,
    collection: 'jobs'        // Collection name (default: 'agendaJobs')
  }),
  processEvery: '30 seconds', // Job polling interval
  maxConcurrency: 20,         // Max concurrent jobs
  defaultConcurrency: 5       // Default per job type
});

PostgreSQL

import { Agenda } from 'agenda';
import { PostgresBackend } from '@agendajs/postgres-backend';

const agenda = new Agenda({
  backend: new PostgresBackend({
    connectionString: 'postgresql://user:pass@localhost:5432/mydb'
  })
});

Redis

import { Agenda } from 'agenda';
import { RedisBackend } from '@agendajs/redis-backend';

const agenda = new Agenda({
  backend: new RedisBackend({
    connectionString: 'redis://localhost:6379'
  })
});

Real-Time Notifications

For faster job processing across distributed systems:

import { Agenda, InMemoryNotificationChannel } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';

const agenda = new Agenda({
  backend: new MongoBackend({ mongo: db }),
  notificationChannel: new InMemoryNotificationChannel()
});

Mixing Storage and Notification Backends

You can use MongoDB for storage while using a different system for real-time notifications:

import { Agenda } from 'agenda';
import { MongoBackend } from '@agendajs/mongo-backend';
import { RedisBackend } from '@agendajs/redis-backend';

// MongoDB for storage + Redis for real-time notifications
const redisBackend = new RedisBackend({ connectionString: 'redis://localhost:6379' });
const agenda = new Agenda({
  backend: new MongoBackend({ mongo: db }),
  notificationChannel: redisBackend.notificationChannel
});

This is useful when you want MongoDB's proven durability and flexible queries for job storage, but need faster real-time notifications across multiple processes.

API Overview

Defining Jobs

// Simple async handler
agenda.define('my-job', async (job) => {
  console.log('Processing:', job.attrs.data);
});

// With options
agenda.define('my-job', async (job) => { /* ... */ }, {
  concurrency: 10,
  lockLimit: 5,
  lockLifetime: 10 * 60 * 1000, // 10 minutes
  priority: 'high'
});

Defining Jobs with Decorators

For a class-based approach, use TypeScript decorators:

import { JobsController, Define, Every, registerJobs, Job } from 'agenda';

@JobsController({ namespace: 'email' })
class EmailJobs {
  @Define({ concurrency: 5 })
  async sendWelcome(job: Job<{ userId: string }>) {
    console.log('Sending welcome to:', job.attrs.data.userId);
  }

  @Every('1 hour')
  async cleanupBounced(job: Job) {
    console.log('Cleaning up bounced emails');
  }
}

registerJobs(agenda, [new EmailJobs()]);
await agenda.start();

// Schedule using namespaced name
await agenda.now('email.sendWelcome', { userId: '123' });

See Decorators Documentation for full details.

Scheduling Jobs

// Run immediately
await agenda.now('my-job', { userId: '123' });

// Run at specific time
await agenda.schedule('tomorrow at noon', 'my-job', data);
await agenda.schedule(new Date('2024-12-25'), 'my-job', data);

// Run repeatedly
await agenda.every('5 minutes', 'my-job');
await agenda.every('0 * * * *', 'my-job'); // Cron syntax

Job Control

// Cancel jobs matching a filter (removes from database)
await agenda.cancel({ name: 'my-job' });
await agenda.cancel({ name: 'my-job', data: { userId: 123 } });

// Cancel ALL jobs unconditionally
await agenda.cancelAll();

// Disable/enable jobs globally (by query)
await agenda.disable({ name: 'my-job' });  // Disable all jobs matching query
await agenda.enable({ name: 'my-job' });   // Enable all jobs matching query

// Disable/enable individual jobs
const job = await agenda.create('my-job', data);
job.disable();
await job.save();

// Progress tracking
agenda.define('long-job', async (job) => {
  for (let i = 0; i <= 100; i += 10) {
    await doWork();
    await job.touch(i); // Report progress 0-100
  }
});

Stopping / Draining

// Stop immediately - unlocks running jobs so other workers can pick them up
await agenda.stop();

// Drain - waits for running jobs to complete before stopping
await agenda.drain();

// Drain with timeout (30 seconds) - for cloud platforms with shutdown deadlines
const result = await agenda.drain(30000);
if (result.timedOut) {
    console.log(`${result.running} jobs still running after timeout`);
}

// Drain with AbortSignal - for external control
const controller = new AbortController();
setTimeout(() => controller.abort(), 30000);
await agenda.drain({ signal: controller.signal });

Use drain() for graceful shutdowns where you want in-progress jobs to finish their work.

Events

agenda.on('start', (job) => console.log('Job started:', job.attrs.name));
agenda.on('complete', (job) => console.log('Job completed:', job.attrs.name));
agenda.on('success', (job) => console.log('Job succeeded:', job.attrs.name));
agenda.on('fail', (err, job) => console.log('Job failed:', job.attrs.name, err));

// Job-specific events
agenda.on('start:send email', (job) => { /* ... */ });
agenda.on('fail:send email', (err, job) => { /* ... */ });

Use fail listeners to capture richer error context, such as stack traces, without storing large payloads in job.attrs.failReason:

agenda.on('fail', async (err, job) => {
	await saveJobError({
		jobId: job.attrs._id,
		jobName: job.attrs.name,
		message: err.message,
		stack: err.stack
	});
});

Custom Backend

For databases other than MongoDB, PostgreSQL, or Redis, implement AgendaBackend:

import { AgendaBackend, JobRepository } from 'agenda';

class SQLiteBackend implements AgendaBackend {
  readonly repository: JobRepository;
  readonly notificationChannel = undefined; // Or implement NotificationChannel

  async connect() { /* ... */ }
  async disconnect() { /* ... */ }
}

const agenda = new Agenda({
  backend: new SQLiteBackend({ path: './jobs.db' })
});

See Custom Backend Driver for details.

Documentation

Related Packages

Official Backend Packages:

Tools:

License

MIT