Skip to content

Conversation

@arv
Copy link
Contributor

@arv arv commented Dec 28, 2025

feat(replicache): Add bulk insertion optimization with putMany

Overview

This PR adds bulk insertion optimization to Replicache's BTree and database layer, significantly improving performance for large batch operations like sync patches.

Changes

Core BTree Changes

packages/replicache/src/btree/node.ts

  • Added putMany() method to DataNodeImpl for efficient merging of sorted entries
  • Added putMany() method to InternalNodeImpl with child grouping and rebalancing
  • Added putManyMergeAndPartition() helper for node rebalancing during bulk operations
  • Extracted binarySearchFrom() to enable optimized searching from a start index
  • Refactored readTreeData() to accept getEntrySize parameter (test helper improvement)

packages/replicache/src/btree/write.ts

  • Added BTreeWrite.putMany() method with two optimized paths:
    • Fast path: Bulk loads empty trees bottom-up using optimal partitioning
    • Slow path: Merges entries into existing trees with efficient rebalancing
  • Validates entries are sorted and converts to sized entries in a single pass
  • Reuses arrays to minimize allocations during tree construction

Database Layer

packages/replicache/src/db/write.ts

  • Added Write.putMany() method that delegates to BTreeWrite.putMany()
  • Handles index updates for all entries before bulk insertion
  • Maintains compatibility with existing put() semantics

Sync Layer Optimization

packages/replicache/src/sync/patch.ts

  • Added optimizePatch() function to eliminate redundant operations:
    • Drops operations before the last clear
    • Merges consecutive operations on the same key
    • Removes pointless del operations after clear
    • Sorts operations by key for optimal bulk loading
  • Modified apply() to use optimized patches with bulk loading
  • Extracted mergeUpdate() helper for update operation handling
  • Added bulkLoadPuts() to handle consecutive put operations efficiently

Performance Impact

The optimization targets common sync patterns:

  1. Initial sync: Loading thousands of entries into an empty tree
  2. Large patches: Applying batches of updates from the server
  3. Snapshot application: Replacing entire datasets

Benchmark Results

Comparison of putMany() vs sequential put() operations:

Scenario Entries Value Size Speedup
Empty tree 100 small 3.36x
Empty tree 100 large 1.49x
Empty tree 1,000 small 5.30x
Empty tree 1,000 large 1.58x
Empty tree 10,000 small 4.15x
Empty tree 10,000 large 1.13x
Construction only 10,000 small 53.73x
Update existing 1,000 mixed 4.47x

Key findings:

  • Small values show 3-5x speedup consistently
  • Construction-only (no flush) shows dramatic 53x improvement
  • Large values benefit less due to serialization overhead
  • Updating existing entries shows 4.5x improvement

Additional benefits:

  • Reduces chunk writes through optimal tree construction
  • Minimizes redundant operations through patch optimization

Testing

New test files:

  • packages/replicache/src/btree/write.bench.ts - Performance benchmarks comparing sequential put() vs putMany()
  • Extensive test coverage in:
    • packages/replicache/src/btree/node.test.ts - 17 new tests for putMany() behavior
    • packages/replicache/src/db/write.test.ts - 3 new tests for database-level putMany()
    • packages/replicache/src/sync/patch.test.ts - 24 new tests for patch optimization

Test scenarios covered:

  • Empty tree bulk loading
  • Merging with existing entries
  • Tree rebalancing and partitioning
  • Index updates
  • Update operation merging
  • Patch optimization edge cases

Compatibility

  • No breaking changes to public APIs
  • Existing put() and del() methods remain unchanged
  • putMany() is an additive optimization that can be adopted incrementally
  • Works with both FormatVersion.V6 and FormatVersion.V7

Implementation Details

Key Algorithm Improvements

  1. Bottom-up tree construction: When building from scratch, constructs the optimal tree structure in a single pass
  2. Batch rebalancing: Groups entries by affected child node and rebalances once per group
  3. Restricted binary search: Uses previous search results to narrow search ranges for sorted input
  4. Patch deduplication: Eliminates redundant operations before applying to the tree

Memory Efficiency

  • Reuses arrays during tree construction to minimize allocations
  • Mutates entries in-place during tree node creation (for immutable node pattern)
  • Batch processes operations to reduce intermediate tree states

Future Work

  • Consider adding delMany() for bulk deletions
  • Explore parallel index updates for large batches

Add efficient bulk insertion methods (putMany) to BTree and database layers,
significantly improving performance for large batch operations like sync patches.

Core Changes

- Add putMany() to BTreeWrite with fast path for empty trees and slow path for merging
- Add putMany() to DataNodeImpl and InternalNodeImpl for node-level bulk operations
- Add Write.putMany() in database layer with index update support
- Add optimizePatch() to eliminate redundant operations in sync patches
- Extract binarySearchFrom() to enable optimized searching from start index

Performance

Benchmark results (putMany vs sequential put):
- **100 entries (small values)**: 3.36x faster
- **1,000 entries (small values)**: 5.30x faster
- **10,000 entries (small values)**: 4.15x faster
- **Construction only (10,000 entries)**: 53.73x faster
- **Update operations (1,000 entries)**: 4.47x faster

Additional benefits:
- Reduces chunk writes through optimal tree construction
- Minimizes redundant operations through patch optimization

Testing

- Add comprehensive test suite covering bulk operations, rebalancing, and edge cases
- Add performance benchmarks comparing sequential put() vs putMany()
- Add patch optimization tests with 24 scenarios

Implementation Details

Fast path (empty tree):

- Builds tree bottom-up using optimal partitioning
- Constructs ideal tree structure in single pass
- Reuses arrays to minimize allocations

Slow path (existing tree):

- Groups entries by affected child nodes
- Performs batch rebalancing per group
- Uses restricted binary search for sorted input

Patch optimization:

- Drops operations before last clear
- Merges consecutive operations on same key
- Removes pointless deletes after clear
- Sorts operations for optimal bulk loading

No breaking changes. Additive optimization compatible with V6 and V7 formats.
@arv arv requested a review from grgbkr December 28, 2025 10:45
@vercel
Copy link

vercel bot commented Dec 28, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
replicache-docs Ready Ready Preview, Comment Dec 28, 2025 11:31am
zbugs Ready Ready Preview, Comment Dec 28, 2025 11:31am

@github-actions
Copy link

github-actions bot commented Dec 28, 2025

🐰 Bencher Report

Brancharv/basic-repl-btree-opt
Testbedself-hosted
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
src/client/custom.bench.ts > big schema📈 view plot
🚷 view threshold
132,508.00 ops/s
(-75.42%)Baseline: 538,987.49 ops/s
-342,400.26 ops/s
(-258.40%)
src/client/zero.bench.ts > basics > All 1000 rows x 10 columns (numbers)📈 view plot
🚷 view threshold
2,433.03 ops/s
(-4.59%)Baseline: 2,550.10 ops/s
2,251.90 ops/s
(92.56%)
src/client/zero.bench.ts > pk compare > pk = N📈 view plot
🚷 view threshold
60,904.00 ops/s
(-6.65%)Baseline: 65,239.87 ops/s
58,983.27 ops/s
(96.85%)
src/client/zero.bench.ts > with filter > Lower rows 500 x 10 columns (numbers)📈 view plot
🚷 view threshold
3,672.00 ops/s
(-5.80%)Baseline: 3,898.09 ops/s
3,530.74 ops/s
(96.15%)
🐰 View full continuous benchmarking report in Bencher

@github-actions
Copy link

github-actions bot commented Dec 28, 2025

🐰 Bencher Report

Brancharv/basic-repl-btree-opt
Testbedself-hosted

🚨 32 Alerts

BenchmarkMeasure
Units
ViewBenchmark Result
(Result Δ%)
Lower Boundary
(Limit %)
1 exists: track.exists(album)Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
13.14 ops/s x 1e3
(-10.48%)Baseline: 14.68 ops/s x 1e3
13.24 ops/s x 1e3
(100.76%)

10 exists (AND)Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
191.51 ops/s x 1e3
(-11.74%)Baseline: 216.98 ops/s x 1e3
201.29 ops/s x 1e3
(105.11%)

10 exists (OR)Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
3.83 ops/s x 1e3
(-8.94%)Baseline: 4.21 ops/s x 1e3
3.85 ops/s x 1e3
(100.59%)

12 exists (AND)Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
160.72 ops/s x 1e3
(-15.97%)Baseline: 191.27 ops/s x 1e3
176.41 ops/s x 1e3
(109.76%)

12 exists (OR)Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
3.11 ops/s x 1e3
(-13.10%)Baseline: 3.57 ops/s x 1e3
3.27 ops/s x 1e3
(105.19%)

12 level nestingThroughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
2.78 ops/s x 1e3
(-10.60%)Baseline: 3.11 ops/s x 1e3
2.86 ops/s x 1e3
(102.60%)

2 exists (AND): track.exists(album).exists(genre)Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
4.94 ops/s x 1e3
(-11.07%)Baseline: 5.55 ops/s x 1e3
5.03 ops/s x 1e3
(101.84%)

3 exists (AND)Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
1.93 ops/s x 1e3
(-9.95%)Baseline: 2.14 ops/s x 1e3
1.94 ops/s x 1e3
(100.66%)

3 exists (OR)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
951.26 ops/s
(-11.46%)Baseline: 1,074.41 ops/s
967.57 ops/s
(101.71%)

5 exists (AND)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
307.87 ops/s
(-8.95%)Baseline: 338.13 ops/s
309.77 ops/s
(100.62%)

5 exists (OR)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
160.32 ops/s
(-10.18%)Baseline: 178.50 ops/s
162.74 ops/s
(101.51%)

Nested 2 levels: track > album > artistThroughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
4.34 ops/s x 1e3
(-9.62%)Baseline: 4.80 ops/s x 1e3
4.39 ops/s x 1e3
(101.19%)

Nested 4 levels: playlist > tracks > album > artistThroughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
709.31 ops/s
(-10.05%)Baseline: 788.56 ops/s
715.70 ops/s
(100.90%)

Nested with filters: track > album > artist (filtered)Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
3.55 ops/s x 1e3
(-11.12%)Baseline: 3.99 ops/s x 1e3
3.60 ops/s x 1e3
(101.51%)

planned: playlist.exists(tracks)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
583.10 ops/s
(-10.45%)Baseline: 651.17 ops/s
608.40 ops/s
(104.34%)

planned: track.exists(album) OR exists(genre)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
147.79 ops/s
(-13.67%)Baseline: 171.19 ops/s
157.98 ops/s
(106.89%)

planned: track.exists(album) where title="Big Ones"Throughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
7.30 ops/s x 1e3
(-8.02%)Baseline: 7.94 ops/s x 1e3
7.41 ops/s x 1e3
(101.46%)

planned: track.exists(album).exists(genre)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
37.42 ops/s
(-9.70%)Baseline: 41.44 ops/s
38.79 ops/s
(103.68%)

planned: track.exists(album).exists(genre) with filtersThroughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
5.06 ops/s x 1e3
(-9.59%)Baseline: 5.60 ops/s x 1e3
5.21 ops/s x 1e3
(102.85%)

planned: track.exists(playlists)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
3.83 ops/s
(-9.14%)Baseline: 4.21 ops/s
3.96 ops/s
(103.54%)

unplanned: playlist.exists(tracks)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
565.03 ops/s
(-10.64%)Baseline: 632.33 ops/s
593.20 ops/s
(104.99%)

unplanned: track.exists(album) OR exists(genre)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
42.31 ops/s
(-10.58%)Baseline: 47.31 ops/s
43.89 ops/s
(103.74%)

unplanned: track.exists(album) where title="Big Ones"Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
52.48 ops/s
(-11.23%)Baseline: 59.12 ops/s
55.22 ops/s
(105.21%)

unplanned: track.exists(album).exists(genre)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
37.26 ops/s
(-9.48%)Baseline: 41.16 ops/s
38.26 ops/s
(102.69%)

unplanned: track.exists(album).exists(genre) with filtersThroughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
53.08 ops/s
(-7.95%)Baseline: 57.66 ops/s
54.50 ops/s
(102.68%)

unplanned: track.exists(playlists)Throughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
3.80 ops/s
(-9.62%)Baseline: 4.21 ops/s
3.97 ops/s
(104.23%)

zpg: all playlistsThroughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
5.52 ops/s
(-4.91%)Baseline: 5.80 ops/s
5.59 ops/s
(101.27%)

zql: all playlistsThroughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
7.08 ops/s
(-12.01%)Baseline: 8.04 ops/s
7.32 ops/s
(103.38%)

zql: push into unlimited queryThroughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
302.57 ops/s x 1e3
(-11.54%)Baseline: 342.04 ops/s x 1e3
306.23 ops/s x 1e3
(101.21%)

zqlite: all playlistsThroughput
operations / second (ops/s)
📈 plot
🚷 threshold
🚨 alert (🔔)
1.68 ops/s
(-10.46%)Baseline: 1.87 ops/s
1.74 ops/s
(103.62%)

zqlite: push into limited query, inside the boundThroughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
3.94 ops/s x 1e3
(-4.60%)Baseline: 4.13 ops/s x 1e3
3.95 ops/s x 1e3
(100.31%)

zqlite: push into unlimited queryThroughput
operations / second (ops/s) x 1e3
📈 plot
🚷 threshold
🚨 alert (🔔)
114.09 ops/s x 1e3
(-11.44%)Baseline: 128.83 ops/s x 1e3
116.62 ops/s x 1e3
(102.22%)

Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
1 exists: track.exists(album)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
13,141.35 ops/s
(-10.48%)Baseline: 14,679.95 ops/s
13,241.24 ops/s
(100.76%)

10 exists (AND)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
191,514.54 ops/s
(-11.74%)Baseline: 216,979.32 ops/s
201,294.73 ops/s
(105.11%)

10 exists (OR)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
3,831.14 ops/s
(-8.94%)Baseline: 4,207.30 ops/s
3,853.76 ops/s
(100.59%)

12 exists (AND)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
160,724.07 ops/s
(-15.97%)Baseline: 191,272.03 ops/s
176,407.46 ops/s
(109.76%)

12 exists (OR)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
3,105.55 ops/s
(-13.10%)Baseline: 3,573.66 ops/s
3,266.76 ops/s
(105.19%)

12 level nesting📈 view plot
🚷 view threshold
🚨 view alert (🔔)
2,784.00 ops/s
(-10.60%)Baseline: 3,113.92 ops/s
2,856.33 ops/s
(102.60%)

2 exists (AND): track.exists(album).exists(genre)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
4,935.86 ops/s
(-11.07%)Baseline: 5,550.47 ops/s
5,026.76 ops/s
(101.84%)

3 exists (AND)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
1,927.87 ops/s
(-9.95%)Baseline: 2,140.80 ops/s
1,940.55 ops/s
(100.66%)

3 exists (OR)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
951.26 ops/s
(-11.46%)Baseline: 1,074.41 ops/s
967.57 ops/s
(101.71%)

5 exists (AND)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
307.87 ops/s
(-8.95%)Baseline: 338.13 ops/s
309.77 ops/s
(100.62%)

5 exists (OR)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
160.32 ops/s
(-10.18%)Baseline: 178.50 ops/s
162.74 ops/s
(101.51%)

Nested 2 levels: track > album > artist📈 view plot
🚷 view threshold
🚨 view alert (🔔)
4,338.74 ops/s
(-9.62%)Baseline: 4,800.68 ops/s
4,390.34 ops/s
(101.19%)

Nested 4 levels: playlist > tracks > album > artist📈 view plot
🚷 view threshold
🚨 view alert (🔔)
709.31 ops/s
(-10.05%)Baseline: 788.56 ops/s
715.70 ops/s
(100.90%)

Nested with filters: track > album > artist (filtered)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
3,546.29 ops/s
(-11.12%)Baseline: 3,989.83 ops/s
3,599.81 ops/s
(101.51%)

planned: playlist.exists(tracks)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
583.10 ops/s
(-10.45%)Baseline: 651.17 ops/s
608.40 ops/s
(104.34%)

planned: track.exists(album) OR exists(genre)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
147.79 ops/s
(-13.67%)Baseline: 171.19 ops/s
157.98 ops/s
(106.89%)

planned: track.exists(album) where title="Big Ones"📈 view plot
🚷 view threshold
🚨 view alert (🔔)
7,304.61 ops/s
(-8.02%)Baseline: 7,941.64 ops/s
7,411.00 ops/s
(101.46%)

planned: track.exists(album).exists(genre)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
37.42 ops/s
(-9.70%)Baseline: 41.44 ops/s
38.79 ops/s
(103.68%)

planned: track.exists(album).exists(genre) with filters📈 view plot
🚷 view threshold
🚨 view alert (🔔)
5,064.01 ops/s
(-9.59%)Baseline: 5,601.32 ops/s
5,208.51 ops/s
(102.85%)

planned: track.exists(playlists)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
3.83 ops/s
(-9.14%)Baseline: 4.21 ops/s
3.96 ops/s
(103.54%)

unplanned: playlist.exists(tracks)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
565.03 ops/s
(-10.64%)Baseline: 632.33 ops/s
593.20 ops/s
(104.99%)

unplanned: track.exists(album) OR exists(genre)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
42.31 ops/s
(-10.58%)Baseline: 47.31 ops/s
43.89 ops/s
(103.74%)

unplanned: track.exists(album) where title="Big Ones"📈 view plot
🚷 view threshold
🚨 view alert (🔔)
52.48 ops/s
(-11.23%)Baseline: 59.12 ops/s
55.22 ops/s
(105.21%)

unplanned: track.exists(album).exists(genre)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
37.26 ops/s
(-9.48%)Baseline: 41.16 ops/s
38.26 ops/s
(102.69%)

unplanned: track.exists(album).exists(genre) with filters📈 view plot
🚷 view threshold
🚨 view alert (🔔)
53.08 ops/s
(-7.95%)Baseline: 57.66 ops/s
54.50 ops/s
(102.68%)

unplanned: track.exists(playlists)📈 view plot
🚷 view threshold
🚨 view alert (🔔)
3.80 ops/s
(-9.62%)Baseline: 4.21 ops/s
3.97 ops/s
(104.23%)

zpg: all playlists📈 view plot
🚷 view threshold
🚨 view alert (🔔)
5.52 ops/s
(-4.91%)Baseline: 5.80 ops/s
5.59 ops/s
(101.27%)

zql: all playlists📈 view plot
🚷 view threshold
🚨 view alert (🔔)
7.08 ops/s
(-12.01%)Baseline: 8.04 ops/s
7.32 ops/s
(103.38%)

zql: edit for limited query, inside the bound📈 view plot
🚷 view threshold
199,573.38 ops/s
(-8.50%)Baseline: 218,111.66 ops/s
196,488.88 ops/s
(98.45%)
zql: edit for limited query, outside the bound📈 view plot
🚷 view threshold
211,395.09 ops/s
(-7.27%)Baseline: 227,964.25 ops/s
195,047.06 ops/s
(92.27%)
zql: push into limited query, inside the bound📈 view plot
🚷 view threshold
106,382.01 ops/s
(-5.55%)Baseline: 112,628.71 ops/s
104,646.90 ops/s
(98.37%)
zql: push into limited query, outside the bound📈 view plot
🚷 view threshold
376,312.54 ops/s
(-8.66%)Baseline: 411,989.10 ops/s
350,038.65 ops/s
(93.02%)
zql: push into unlimited query📈 view plot
🚷 view threshold
🚨 view alert (🔔)
302,571.68 ops/s
(-11.54%)Baseline: 342,040.13 ops/s
306,232.19 ops/s
(101.21%)

zqlite: all playlists📈 view plot
🚷 view threshold
🚨 view alert (🔔)
1.68 ops/s
(-10.46%)Baseline: 1.87 ops/s
1.74 ops/s
(103.62%)

zqlite: edit for limited query, inside the bound📈 view plot
🚷 view threshold
74,611.39 ops/s
(-5.77%)Baseline: 79,180.40 ops/s
70,165.16 ops/s
(94.04%)
zqlite: edit for limited query, outside the bound📈 view plot
🚷 view threshold
75,059.97 ops/s
(-5.67%)Baseline: 79,573.06 ops/s
66,373.92 ops/s
(88.43%)
zqlite: push into limited query, inside the bound📈 view plot
🚷 view threshold
🚨 view alert (🔔)
3,936.03 ops/s
(-4.60%)Baseline: 4,125.75 ops/s
3,948.40 ops/s
(100.31%)

zqlite: push into limited query, outside the bound📈 view plot
🚷 view threshold
82,443.81 ops/s
(-8.97%)Baseline: 90,571.62 ops/s
82,059.70 ops/s
(99.53%)
zqlite: push into unlimited query📈 view plot
🚷 view threshold
🚨 view alert (🔔)
114,089.95 ops/s
(-11.44%)Baseline: 128,828.62 ops/s
116,619.46 ops/s
(102.22%)

🐰 View full continuous benchmarking report in Bencher

@github-actions
Copy link

🐰 Bencher Report

Brancharv/basic-repl-btree-opt
TestbedLinux
Click to view all benchmark results
BenchmarkFile SizeBenchmark Result
kilobytes (KB)
(Result Δ%)
Upper Boundary
kilobytes (KB)
(Limit %)
zero-package.tgz📈 view plot
🚷 view threshold
1,779.16 KB
(+0.21%)Baseline: 1,775.44 KB
1,810.95 KB
(98.24%)
zero.js📈 view plot
🚷 view threshold
243.81 KB
(+0.75%)Baseline: 242.00 KB
246.84 KB
(98.77%)
zero.js.br📈 view plot
🚷 view threshold
66.90 KB
(+0.68%)Baseline: 66.45 KB
67.78 KB
(98.71%)
🐰 View full continuous benchmarking report in Bencher

@rajczi
Copy link

rajczi commented Jan 16, 2026

We've now been using this build of Replicache in our expo react native mobile app using the sqlite kvStore against op-sqlite@15.2 for quite a while and are very happy with it. It is saving us over half of our initial sync snapshot time (download complete to ready).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants