Skip to content

Benchmarks ​

Conveyor includes a benchmark suite to measure throughput of core operations. All benchmarks run on MemoryStore with Deno to isolate queue logic from I/O overhead.

Run benchmarks:

bash
deno task bench

Summary ​

BenchmarkResultNotes
Queue.add (500 jobs)2.68ms total, ~373 ops/secSequential adds
fetchNextJob (from 1,000)188us per fetchAtomic lock + fetch
Batch worker (20 jobs, batch=20)~1s20x throughput vs single
Batch worker (20 jobs, batch=1)~20sBaseline comparison

Key Findings ​

Queue.add Throughput ​

Adding 500 jobs sequentially completes in approximately 2.68ms on the MemoryStore. This measures the overhead of job data creation, deduplication checks, and store persistence.

fetchNextJob Latency ​

Fetching the next job from a queue of 1,000 waiting jobs takes approximately 188 microseconds. This includes the atomic select-and-lock operation. In PostgreSQL, this translates to a single SELECT ... FOR UPDATE SKIP LOCKED query.

Batch Processing ​

Batch processing provides significant throughput improvements when jobs can be processed together:

  • Batch size 20: Processing 20 jobs takes approximately 1 second (all jobs processed in a single batch call).
  • Batch size 1: The same 20 jobs take approximately 20 seconds when processed one at a time.
  • Improvement: ~95% faster with batching enabled (20x throughput).

This makes batching ideal for operations that benefit from bulk I/O, such as database inserts, API calls with batch endpoints, or bulk email sending.

Store-Specific Performance ​

The benchmarks above use MemoryStore to measure pure queue overhead. Real-world performance depends on the store backend:

Storeadd()fetchNextJob()Notes
MemoryStoreFastestFastestNo I/O, ideal for benchmarks and tests
PgStoreNetwork-boundNetwork-boundPerformance depends on network latency and PostgreSQL configuration
SqliteStoreDisk-boundDisk-boundWAL mode helps; performance depends on disk speed

PostgreSQL Optimization Tips ​

  • Use connection pooling for high-throughput producers.
  • Place the database close to your workers (same datacenter/region).
  • Tune work_mem and shared_buffers for your workload.
  • Use addBulk() instead of individual add() calls for batch inserts.

SQLite Optimization Tips ​

  • Use an SSD for the database file.
  • WAL mode is enabled automatically -- do not disable it.
  • For write-heavy workloads, consider PostgreSQL instead.
  • Keep the database file on a local filesystem (not NFS or network mounts).

Running Your Own Benchmarks ​

The benchmark suite is in the benchmarks/ directory. You can add custom benchmarks:

typescript
import { Queue, Worker } from '@conveyor/core';
import { MemoryStore } from '@conveyor/store-memory';

const store = new MemoryStore();
await store.connect();

const queue = new Queue('bench', { store });

const start = performance.now();
for (let i = 0; i < 1000; i++) {
  await queue.add('task', { index: i });
}
const elapsed = performance.now() - start;

console.log(`1000 adds in ${elapsed.toFixed(2)}ms`);

await queue.close();
await store.disconnect();

Released under the MIT License.