@eddyq/queue

中文文档
0.2.0发布于 2026/11/05MITTypeScriptCommonJS零依赖构建来源已验证近 30 天下载 2899

Node.js client for eddyq — a Rust + Postgres job queue

原始使用文档
文档目录8 节
  1. Install
  2. Quick start
  3. Migrations
  4. Retry convention
  5. Cancellation
  6. Admin surface
  7. Tuning
  8. License

@eddyq/queue

Node.js client for eddyq — a Rust job queue that runs on Postgres, Redis, or both.

Three classes you can import:

  • Eddyq — Postgres backend. Transactional enqueue, durable batches, migrations.
  • EddyqRedis — Redis backend (via Redis Functions). ~70k jobs/sec bulk ingest, { every: ms } schedules, no migrations.
  • EddyqApp — multi-backend container. Route per-queue across both in one process (e.g. webhooks → Redis, payments → Postgres). See examples/redis-basic/multi.mjs.

See the main README for backend tradeoffs and benchmark numbers, or @eddyq/wakeboard for the dashboard UI.

Install

pnpm add @eddyq/queue

We recommend pnpm or yarn over npm — see the npm caveat for why NAPI-RS + npm has rough edges.

Quick start

import { Eddyq, CancelError, RetryError } from "@eddyq/queue";

const q = await Eddyq.connect(process.env.DATABASE_URL);

q.work("send.email", async ({ payload, id, attempt, signal }) => {
  const res = await fetch(payload.url, { signal }); // signal flips on shutdown

  if (res.status === 429) {
    const after = Number(res.headers.get("retry-after") ?? 60) * 1000;
    throw new RetryError("rate limited", { delayMs: after });
  }
  if (!res.ok && attempt >= 3) {
    throw new CancelError("permanent 5xx; giving up");
  }

  return { bytes: (await res.text()).length };   // stored in eddyq_jobs.result
});

await q.start();

// ... your app runs ...

await q.shutdown(10_000);   // 10s grace; fires abort signals

Migrations

eddyq does not auto-migrate at boot — slow migrations would stall every replica's startup. Run them as an explicit deploy step before booting workers.

The package ships a CLI:

DATABASE_URL=postgres://... npx eddyq migrate run
DATABASE_URL=postgres://... npx eddyq migrate list
DATABASE_URL=postgres://... npx eddyq migrate down --max-steps 1 --confirm

Or call from code:

import { Eddyq } from '@eddyq/queue';
const q = await Eddyq.connect(process.env.DATABASE_URL);
const report = await q.migrate();
console.log("applied:", report.applied.map(m => `${m.version}:${m.name}`));
await q.close();

eddyq.start() refuses to boot if migrations are pending. To bypass when schema is managed out-of-band, pass { skipMigrationCheck: true }.

migrate() is idempotent and holds a pg_advisory_lock per migration line, so running it twice (or from two deploy hosts at once) serializes safely.

Retry convention

ThrownBehaviour
new CancelError("reason")Permanent fail. No retry, regardless of maxAttempts.
new RetryError("reason", { delayMs: 60_000 })Retry at now + delayMs. Overrides default backoff.
any other ErrorDefault exponential backoff, up to maxAttempts.

Every failure persists the full { name, message, stack, directive, retryDelayMs } in eddyq_jobs.errors[].

Cancellation

Every handler receives call.signal — an AbortSignal that fires when eddyq.shutdown() is called. Pass it along:

q.work("download", async ({ payload, signal }) => {
  const res = await fetch(payload.url, { signal });
  return res.json();
});

shutdown(gracefulTimeoutMs?) defaults to 30 000 ms. It broadcasts abort to all in-flight handlers, waits up to the timeout, then force-cancels the runtime tasks. Orphaned jobs are recovered by the heartbeat sweeper on the next surviving worker.

Admin surface

CallWhat it does
enqueue(kind, payload, opts?)Enqueue a single job. opts covers uniqueKey, priority, scheduledAtMs, groupKey, tags, metadata, maxAttempts.
cancel(jobId)Cancel a pending job. No-op on running/finalized.
setGroupConcurrency(key, max)Cap concurrent jobs sharing groupKey.
setGroupRate(key, count, periodMs)Token-bucket rate limit per group.
pauseGroup / resumeGroupPause dispatch for a group.
setQueueConcurrency(queue, max)Cap total running across all worker processes.
pauseQueue / resumeQueueSame, for a named queue.
setQueueTimeout(queue, ms | null)Default per-job timeout on this queue.
addSchedule(name, cron, kind, payload, opts?)Upsert a cron schedule. 6-field cron (sec min hour dom month dow). opts covers priority, maxAttempts.
syncSchedules(declared)Reconcile DB against a declared list — upserts each entry, deletes any schedule not in the list. Idempotent; the boot-time pattern when schedules are managed in code.
removeSchedule(name) / setScheduleEnabled(name, enabled)Delete or toggle a schedule.
listSchedules() / listNamedQueues() / listGroups()Read the admin state.
getStats() / listJobs(filter?, pagination?)Dashboard-oriented reads.
migrate() / migrateDown(n) / migrationStatus()Schema control.

Tuning

Defaults are sized for typical workloads — most users should leave them alone. Pass any of these to start() when you need to deviate:

await q.start({
  sweepIntervalMs:        30_000,    // heartbeat sweeper cadence
  staleAfterMs:           60_000,    // when a running job is considered orphaned
  heartbeatIntervalMs:    15_000,    // worker → DB heartbeat (must be ≪ staleAfterMs)
  cleanupIntervalMs:     300_000,    // retention sweep cadence
  completedRetentionSecs: 24 * 3600, // delete completed older than this
  failedRetentionSecs:    7 * 86400,
  cancelledRetentionSecs: 7 * 86400, // pass -1 to keep forever
  leaderLeaseSecs:        30,        // single-leader maintenance lease
  fetchPollIntervalMs:    1_000,     // ignored unless poll-only
});

When to touch them:

  • High throughput (millions of completed jobs/day) — drop completedRetentionSecs to a few hours so cleanup keeps up.
  • Long handlers (jobs that legitimately run > 60s) — raise staleAfterMs past your worst-case duration so the sweeper doesn't reclaim live jobs. Also raise heartbeatIntervalMs proportionally.
  • Many replicas (50+ pods) — raise sweepIntervalMs so you're not hammering the sweep query from every pod.

License

MIT

版本与发布记录

@eddyq/queue 共发布 28 个版本,首次发布于 2026/04/26;过去 12 个月发布了 28 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.2.0latest2026/11/058 天前
0.1.142026/10/3014 天前
0.1.132026/10/2915 天前
0.1.122026/10/2915 天前
0.1.112026/10/2816 天前
0.1.102026/10/2420 天前

关于 @eddyq/queue 的常见问题

@eddyq/queue 是什么?

Node.js client for eddyq — a Rust + Postgres job queue

如何安装 @eddyq/queue?

在项目目录运行 npm install @eddyq/queue,或使用 pnpm add / yarn add / bun add。它还提供命令行工具 eddyq,可以用 npx eddyq 直接运行。

@eddyq/queue 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@eddyq/queue 支持 ESM 还是 CommonJS?

以 CommonJS 形式发布,可以直接 require();在 ES Module 项目中也可以用 import 引入(Node.js 会把 module.exports 作为默认导出)。

@eddyq/queue 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=20。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@eddyq/queue 可以商用吗?

@eddyq/queue 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@eddyq/queue 的下载量有多少?

根据 npm 官方下载统计,@eddyq/queue 在 2026/10/13 – 2026/11/11 这 30 天内被下载 2,899 次,日均约 97 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@eddyq/queue 还在维护吗?

最新版本 0.2.0 发布于 2026/11/05(8 天前);至今共发布 28 个版本,其中过去 12 个月发布了 28 个;首次发布于 2026/04/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。