Skip to main content

GoatDB Benchmarks

GoatDB's benchmarks provide a performance comparison between GoatDB's different operational modes and SQLite. These benchmarks help understand the performance characteristics and tradeoffs of each approach.

We're comparing GoatDB to SQLite because SQLite is widely considered the gold standard for embedded databases, offering an excellent balance of features, performance, and reliability.

SQLite is built using classic RDBMS techniques and especially shines when complex queries on larger-than-memory datasets are needed. However, when implementing synchronization mechanisms and security controls for offline writes, GoatDB is able to offer competitive performance.

The most basic trade-off between the two systems is that GoatDB first loads the entire dataset into memory before any operations can be performed. This comes with its own unique set of trade-offs. It's important to note that GoatDB isn't trying to replace SQLite, but rather carve its own niche for specific use cases where in-memory operations, offline capabilities, and cryptographic security are prioritized over handling larger-than-memory datasets.

Replicating these benchmarks on your machine is easy. Just clone the GoatDB repository and run:

deno task bench

All benchmarks were performed on the following configuration:

  • CPU: Intel(R) Core(TM) i7-8850H CPU @ 2.60GHz
  • Runtime: Deno 2.2.3 (x86_64-apple-darwin)

Summary

First, let's compare different operational modes that guarantee durability. Note that GoatDB's default mode cryptographically signs each commit (write) for security, while trusted mode skips these security controls for better performance:

BenchmarkGoatDB (Default)GoatDB (Trusted)SQLite
Create instance5.1 ms4.6 ms203.2 µs
Open repository (empty)1.4 ms1.5 ms1.1 ms
Open repository (100k items)738.4 ms721.5 ms186.3 µs
Create single item3.0 ms2.0 ms815.6 µs
Read item by path2.0 µs2.5 µs67.0 µs
Update item1.8 ms327.5 µs780.6 µs
Bulk create 100 items98.1 ms67.1 ms1.5 ms
Bulk read 100 items421.8 µs468.5 µs1.7 ms
Simple query264.1 µs324.2 µs149.0 µs
Complex query with sort144.1 µs207.2 µs97.1 µs
Repository operations: count4.7 µs4.6 µs64.7 µs
Repository operations: keys7.9 µs7.9 µs81.6 µs

When relaxed durability is acceptable, both GoatDB and SQLite can skip the fsync call to achieve much faster performance. Note that with SQLite, you risk database corruption if the system crashes during a write, while GoatDB will simply discard any incomplete writes.

BenchmarkGoatDB (Fast)SQLite (synchronous = OFF)
Create instance5.5 ms160.3 µs
Open repository (empty)1.4 ms507.4 µs
Open repository (100k items)717.1 ms196.3 µs
Create single item91.3 µs701.4 µs
Read item by path2.6 µs63.9 µs
Update item24.9 µs521.8 µs
Bulk create 100 items11.5 ms900.0 µs
Bulk read 100 items43.9 µs1.8 ms
Simple query293.7 µs116.1 µs
Complex query with sort159.1 µs92.1 µs
Repository operations: count3.9 µs58.5 µs
Repository operations: keys7.1 µs67.4 µs

GoatDB is a memory-first database built on an append-only distributed commit graph stored as a log of commits on disk. This design currently requires the entire commit graph to be loaded into memory before any operations can be performed.

Opening a repository takes time proportional to the number of commits it contains. While the benchmark shows GoatDB is significantly slower than SQLite when opening a repository with 100k items (721ms vs 186µs), it's important to note that if we actually read the entire table's contents into memory, GoatDB is only about 5x slower than SQLite's SELECT * operation (721ms vs 136ms).

The performance breakdown of repository opening reveals that bringing the raw log data into memory is the least time-consuming part (approximately 10% of the total time). The majority is spent on deserializing and constructing the in-memory representation of the commit graph—a particularly challenging workload for modern JavaScript garbage collectors.

To address this performance bottleneck, we are developing a zero-copy format (see GitHub issue #36) that will significantly reduce this overhead and bring opening times much closer to SQLite's performance.

GoatDB uses a different scaling approach than traditional databases. Rather than growing a single large database, it employs application-level sharding with multiple medium-sized repositories that sync independently. Each user or data group has its own repository, enabling horizontal scaling and efficient client-server synchronization. This architecture provides natural scalability for multi-user applications without complex manual sharding.

SQLite shines in query performance with its decades of battle-tested optimizations, though GoatDB's incremental queries perform competitively in real-world scenarios despite their simpler implementation. While GoatDB is written in TypeScript and SQLite in C, the benchmark differences primarily stem from fundamentally different architectures rather than language choice, as modern JavaScript runtimes are quite competitive on raw performance. GoatDB prioritizes distributed operation and offline-first capabilities, while SQLite's B-tree implementation has been refined since 2000 for traditional database performance, reflecting their different design goals.

Synchronization Latency

The benchmarks above focus on local database operations. For distributed synchronization between peers, GoatDB exhibits different performance characteristics:

MetricTypical RangeNotes
Single item sync700-1000msApplication-perceived latency
Concurrent sync (10 items)1000-1400ms averageIncludes queuing overhead
Success rate under load>95%10 concurrent operations

These measurements represent end-to-end latency from item creation on one peer to API availability on another peer, including GoatDB's polling-based sync architecture, Bloom filter exchanges, and processing overhead. Pure network transmission comprises approximately 50-200ms of the total latency.

Synchronization performance prioritizes consistency and offline-first design over minimal latency, making GoatDB well-suited for collaborative applications but less optimal for high-frequency real-time use cases.

Default Mode

The benchmarks below show performance in Default mode, which includes all security and cryptographic controls. While these controls add some performance overhead, they enable critical features such as:

  • Cryptographically verified data integrity
  • Secure multi-user collaboration
  • Clients act as active replicas automatically and securely restoring a crashed server
  • Protection against unauthorized offline data modifications
  • Replicated tamper-proof audit trail of all changes
BenchmarkAveragep75p99p995
Create instance5.1 ms5.4 ms9.1 ms9.1 ms
Open repository (empty)1.4 ms1.5 ms1.8 ms1.8 ms
Open repository (100k items)738.4 ms773.3 ms809.0 ms809.0 ms
Create single item3.0 ms3.2 ms3.2 ms3.2 ms
Read item by path2.0 µs2.0 µs2.5 µs2.5 µs
Update item1.8 ms1.9 ms2.2 ms2.2 ms
Bulk create 100 items98.1 ms77.5 ms402.4 ms402.4 ms
Bulk read 100 items421.8 µs443.9 µs487.5 µs487.5 µs
Simple query264.1 µs264.1 µs1.3 ms1.3 ms
Complex query with sort144.1 µs157.3 µs215.2 µs215.2 µs
Repository operations: count4.7 µs4.8 µs7.6 µs7.6 µs
Repository operations: keys7.9 µs8.2 µs9.2 µs9.2 µs

Trusted Mode

Trusted mode bypasses cryptographic verification and security controls for improved performance. This mode is suitable for applications where security is handled at a different layer or in trusted environments, such as microservices running in the cloud without direct client interaction.

BenchmarkAveragep75p99p995
Trusted: Create instance4.6 ms4.9 ms7.7 ms7.7 ms
Trusted: Open repository (empty)1.5 ms1.5 ms4.2 ms4.2 ms
Trusted: Open repository (100k items)721.5 ms712.1 ms837.5 ms837.5 ms
Trusted: Create single item2.0 ms2.1 ms2.5 ms2.5 ms
Trusted: Read item by path2.5 µs2.7 µs3.3 µs3.3 µs
Trusted: Update item327.5 µs373.5 µs441.0 µs441.0 µs
Trusted: Bulk create 100 items67.1 ms78.8 ms80.6 ms80.6 ms
Trusted: Bulk read 100 items468.5 µs462.9 µs952.9 µs952.9 µs
Trusted: Simple query324.2 µs333.3 µs382.0 µs382.0 µs
Trusted: Complex query with sort207.2 µs222.7 µs242.8 µs242.8 µs
Trusted: Repository operations: count4.6 µs4.5 µs7.1 µs7.1 µs
Trusted: Repository operations: keys7.9 µs8.6 µs9.1 µs9.1 µs

Fast Mode

Fast mode is similar to trusted mode, but with one key difference: the code doesn't wait for updates to be persisted to local disk before acknowledging completion. Instead, GoatDB persists updates in the background, writing to both the local disk and remote server concurrently. This mode is particularly useful for caching applications in backend environments or for trusted systems where performance is the highest priority while still maintaining eventual durability. Fast mode is ideal when you need maximum throughput for high-volume operations while accepting a small risk of data loss in case of sudden system failure.

Even in Fast mode, GoatDB maintains data integrity through its append-only commit graph architecture. This design provides inherent resistance to corruption - in the event of a system crash during write operations, the database simply trims the log to the last valid commit point. Unlike traditional databases where crashes can lead to complex recovery scenarios or data corruption, GoatDB's approach ensures that the database always remains in a consistent state. This structural safeguard works alongside the performance optimizations of Fast mode, providing both speed and reliability without compromising data integrity, at the expense of decreased durability.

BenchmarkAveragep75p99p995
Fast: Create instance5.5 ms5.5 ms14.7 ms14.7 ms
Fast: Open repository (empty)1.4 ms1.5 ms1.6 ms1.6 ms
Fast: Open repository (100k items)717.1 ms716.1 ms816.3 ms816.3 ms
Fast: Create single item91.3 µs95.7 µs132.3 µs132.3 µs
Fast: Read item by path2.6 µs2.8 µs3.2 µs3.2 µs
Fast: Update item24.9 µs24.7 µs57.9 µs57.9 µs
Fast: Bulk create 100 items11.5 ms7.3 ms86.4 ms86.4 ms
Fast: Bulk read 100 items43.9 µs44.3 µs78.2 µs78.2 µs
Fast: Simple query293.7 µs314.5 µs414.0 µs414.0 µs
Fast: Complex query with sort159.1 µs176.0 µs263.1 µs263.1 µs
Fast: Repository operations: count3.9 µs4.1 µs4.7 µs4.7 µs
Fast: Repository operations: keys7.1 µs7.3 µs8.1 µs8.1 µs

SQLite Comparison

SQLite is currently the leading choice in embedded databases, known for its reliability and performance. While SQLite doesn't provide any security controls nor synchronizes across devices, its benchmark is provided here for reference purposes.

BenchmarkAveragep75p99p995
SQLite: Create instance203.2 µs195.5 µs396.0 µs506.9 µs
SQLite: Create table1.1 ms1.1 ms2.0 ms2.2 ms
SQLite: Open database (100k items)186.3 µs199.9 µs332.2 µs342.4 µs
SQLite: Read 100k items138.7 ms139.7 ms153.3 ms153.3 ms
SQLite: Create single item815.6 µs889.9 µs1.2 ms1.2 ms
SQLite: Read item by ID67.0 µs71.6 µs121.7 µs126.0 µs
SQLite: Update item780.6 µs891.5 µs1.2 ms1.3 ms
SQLite: Bulk create 100 items1.5 ms1.5 ms2.3 ms3.0 ms
SQLite: Bulk read 100 items1.7 ms1.7 ms2.3 ms2.4 ms
SQLite: Simple query149.0 µs155.8 µs291.5 µs491.8 µs
SQLite: Complex query with sort97.1 µs101.8 µs169.7 µs233.7 µs
SQLite: Count operation64.7 µs65.1 µs131.7 µs366.9 µs
SQLite: Keys operation81.6 µs84.9 µs197.0 µs211.6 µs

synchronous = OFF

A somewhat similar comparison to GoatDB's Fast mode would be with SQLite's synchronous = OFF setting. This setting disables the durability guarantees of SQLite, allowing the database to operate in a more performant mode, while risking a corruption of the database in case of sudden system failure.

BenchmarkAveragep75p99p995
SQLite-fast-unsafe: Create instance160.3 µs171.9 µs277.5 µs313.4 µs
SQLite-fast-unsafe: Create table507.4 µs526.2 µs871.4 µs982.0 µs
SQLite-fast-unsafe: Open database (100k)196.3 µs214.5 µs355.5 µs390.1 µs
SQLite-fast-unsafe: Read 100k items136.5 ms141.0 ms149.7 ms149.7 ms
SQLite-fast-unsafe: Create single item701.4 µs877.2 µs1.4 ms1.4 ms
SQLite-fast-unsafe: Read item by ID63.9 µs67.2 µs142.1 µs179.0 µs
SQLite-fast-unsafe: Update item521.8 µs534.5 µs1.3 ms1.3 ms
SQLite-fast-unsafe: Bulk create 100 items900.0 µs919.0 µs1.4 ms1.5 ms
SQLite-fast-unsafe: Bulk read 100 items1.8 ms1.8 ms2.4 ms2.6 ms
SQLite-fast-unsafe: Simple query116.1 µs124.1 µs176.4 µs184.3 µs
SQLite-fast-unsafe: Complex query w/sort92.1 µs96.1 µs160.0 µs201.3 µs
SQLite-fast-unsafe: Count operation58.5 µs63.8 µs117.6 µs122.7 µs
SQLite-fast-unsafe: Keys operation67.4 µs71.5 µs127.3 µs138.7 µs