ParqueDB

Performance Benchmarks

Comprehensive performance benchmarks for ParqueDB operations across different scales and environments

This document provides comprehensive performance benchmarks for ParqueDB operations across different scales and environments.

Table of Contents

Overview

ParqueDB is designed for efficient read-heavy workloads with the following performance characteristics:

  • Sub-millisecond reads for indexed lookups in Cloudflare Workers
  • Columnar storage enables reading only necessary columns
  • Row-group statistics enable predicate pushdown
  • Secondary indexes (Hash, SST, FTS) for accelerated queries
  • Variant shredding reduces I/O by 2-10x for common queries

Test Environment

  • Node.js: v20.x or later
  • Cloudflare Workers: workerd runtime via vitest-pool-workers
  • Datasets: Synthetic + real-world (IMDB, O*NET, UNSPSC)

Running Benchmarks

Quick Start

# Run all benchmarks
npm run bench

# Run specific benchmark suites
npm run bench:crud        # CRUD operations
npm run bench:queries     # Query patterns
npm run bench:parquet     # Parquet I/O
npm run bench:relationships  # Graph traversal
npm run bench:examples    # Real-world examples

# Run the unified benchmark script
npm run benchmark         # Comprehensive benchmark report

E2E Benchmarks (Deployed Worker)

# Test against deployed worker
node scripts/e2e-benchmark.mjs --url=https://parquedb.workers.do

# Local development
node scripts/e2e-benchmark.mjs --url=http://localhost:8787 --verbose

CRUD Operations

Single Operations

OperationEntitiesMean (ms)P95 (ms)Ops/sec
Create single11.02.01,000
Get by ID10.71.01,400
Update ($set)11.12.0900
Update ($inc)11.93.0530
Delete single10.81.51,250

Batch Operations

OperationCountMean (ms)P95 (ms)Entities/sec
Batch create10015-25304,000-6,600
Batch create1,000150-2503004,000-6,600
Batch create10,0001,500-2,5003,0004,000-6,600
Batch delete10050-801001,250-2,000

Update Operators

OperatorMean (ms)Description
$set (single field)1.0Set field value
$set (multiple fields)1.2Set multiple fields
$inc1.9Increment numeric field
$push2.0Append to array
$addToSet2.1Add unique to array
$pull2.2Remove from array
$unset1.0Remove field

Query Performance

Filter Operations

Query Type1K Entities10K Entities100K Entities
Full scan5ms45ms450ms
Equality filter3ms25ms200ms
Range filter ($gt, $lt)4ms30ms250ms
$in operator3ms28ms220ms
$and (2 conditions)4ms35ms280ms
$or (2 conditions)5ms40ms320ms
Complex nested6ms50ms400ms
$regex8ms80ms800ms

Indexed Queries

With secondary indexes, query performance improves significantly:

Query TypeWithout IndexWith Hash IndexWith SST Index
Equality (100K)200ms0.5ms1ms
Range (100K)250msN/A2ms
Point lookup (100K)200ms0.3ms0.5ms

Sort Performance

Sort Type1K Entities10K Entities100K Entities
Single field (numeric)2ms20ms200ms
Single field (string)3ms30ms300ms
Multi-field (2)4ms40ms400ms
Multi-field (3)5ms50ms500ms
Indexed field1ms8ms80ms

Pagination

MethodPage 1Page 10Page 100Page 500
Offset (limit 20)2ms4ms20ms100ms
Cursor-based2ms2ms2ms2ms

Recommendation: Use cursor-based pagination for deep pagination.

Scalability

Scale Tests

Performance across different dataset sizes:

ScaleFind AllFilterSortCountGroup By
100<1ms<1ms<1ms<1ms<1ms
1,0005ms3ms5ms2ms4ms
10,00045ms25ms50ms15ms35ms
100,000450ms200ms500ms100ms350ms

Memory Usage

Dataset SizeHeap Per EntityQuery Result Memory
Small (100B)~200 bytes~250 bytes/entity
Medium (1KB)~1.2 KB~1.5 KB/entity
Large (10KB)~11 KB~12 KB/entity

Tip: Use projections to reduce memory usage for large result sets.

Index Performance

Secondary Index Types

ParqueDB supports three types of secondary indexes:

  1. Hash Index: O(1) equality lookups
  2. SST Index: Range queries with sorted structure
  3. FTS Index: Full-text search with TF-IDF scoring

Index Build Time

Index Type10K Docs100K Docs500K Docs
Hash15ms120ms600ms
SST25ms200ms1,000ms
FTS100ms800ms4,000ms

Index Lookup Performance

OperationHash IndexSST IndexFull Scan
Equality (100K)0.1ms0.3ms200ms
Range (100K)N/A2ms250ms
Prefix (100K)N/A1ms300ms
Query10K Docs50K Docs100K Docs
Single term0.5ms2ms4ms
Two terms1ms4ms8ms
Phrase2ms8ms15ms
Rare term0.2ms0.5ms1ms

Cloudflare Workers Performance

R2 Storage Operations

OperationMean (ms)P95 (ms)Ops/sec
Write small (100B)1.12.0943
Write medium (1KB)0.52.02,174
Write large (10KB)<1<1>5,000
Read small (100B)0.21.04,167
Read medium (1KB)0.21.05,000
Read large (10KB)0.21.05,556
HEAD (existence)0.11.012,500
List 10 items0.11.016,667

Durable Objects

OperationMean (ms)P95 (ms)Ops/sec
Cold start3.04.0333
Warm create1.12.0877
Warm get by ID0.71.01,389
Warm update1.12.0943
SQLite query0.71.01,500

End-to-End Operations

OperationMean (ms)P95 (ms)
Full CRUD cycle3.65.0
DO + R2 sequential2.65.0
DO + R2 parallel2.95.0

Key Observations

  1. R2 local operations are extremely fast (<1ms for most ops)
  2. DO cold start adds ~3ms overhead vs warm operations (~1ms)
  3. Batched operations (Promise.all) are ~2x faster than sequential
  4. Payload size has minimal impact on DO create latency

Storage Comparisons

Parquet vs JSON

Format1K Entities10K Entities100K Entities
JSON (raw)850 KB8.5 MB85 MB
JSON (gzip)120 KB1.2 MB12 MB
Parquet (snappy)95 KB950 KB9.5 MB
Parquet (gzip)80 KB800 KB8 MB

Storage Savings: Parquet provides 8-10x compression vs raw JSON.

Variant Shredding Benefits

The V3 "dual variant" architecture shreds hot fields for efficient column projection:

Query TypeWithout ShreddingWith ShreddingSpeedup
Full entity100ms100ms1x
Index columns only100ms5ms20x
2 columns100ms15ms6.7x
4 columns100ms30ms3.3x

Compression Comparison

CodecWrite (ms)Read (ms)SizeRatio
None50302.0 MB1.0x
Snappy55350.8 MB2.5x
GZIP120800.5 MB4.0x
LZ452320.7 MB2.9x

Recommendation: Use Snappy for best balance of speed and compression.

Performance Targets

ParqueDB targets the following SLAs:

OperationTarget (p50)Target (p99)Status
Get by ID5ms20msAchieved
Find (indexed)20ms100msAchieved
Find (scan)100ms500msAchieved
Create10ms50msAchieved
Update15ms75msAchieved
Delete10ms50msAchieved
Relationship traverse50ms200msAchieved
FTS search20ms100msAchieved

Benchmark Architecture

Test Files

tests/benchmarks/
  crud.bench.ts         # CRUD operations
  queries.bench.ts      # Query patterns
  scalability.bench.ts  # Scale tests (100-100K)
  parquet.bench.ts      # Parquet I/O
  relationships.bench.ts # Graph traversal
  setup.ts              # Utilities

scripts/
  benchmark.mjs         # Unified benchmark runner
  benchmark-indexes.mjs # Index benchmarks
  benchmark-v3.mjs      # V3 architecture tests
  e2e-benchmark.mjs     # Deployed worker tests

Datasets Used

  1. Synthetic: Generated entities with varied field types
  2. IMDB: 100K titles, 50K people, 200K cast relationships
  3. O*NET: 1,000 occupations, skills, and relationships
  4. UNSPSC: Product classification hierarchy

Running Custom Benchmarks

import { benchmark, calculateStats, formatStats } from './tests/benchmarks/setup'

const stats = await benchmark('my operation', async () => {
  // Your code here
}, { iterations: 100, warmupIterations: 10 })

console.log(formatStats(stats))

Continuous Benchmarking

Benchmarks run automatically on:

  • Every push to main branch
  • Manual workflow dispatch

Results are stored as GitHub artifacts and tracked over time via github-action-benchmark.

Viewing Results

  1. Go to Actions > Benchmark workflow
  2. Download the benchmark-results artifact
  3. Or view trends on the gh-pages branch at /dev/bench

Contributing

When adding new benchmarks:

  1. Add test file to tests/benchmarks/
  2. Follow naming convention: *.bench.ts
  3. Use provided utilities from setup.ts
  4. Document expected performance in this file
  5. Add to CI workflow if needed

Last updated: February 2026

On this page