16 of the server, and 14 of the storage
Telemetry cost is the servers that take it in and the storage that keeps it. Quiver needs far less of both, on object storage that typically costs less per GB than local disk.
Telemetry will be used by AI agents in the future, not by people typing into a search box
Telemetry is written all day in huge volumes, never changed once written, and read rarely: AI agents query it in short bursts during an investigation, not dashboards refreshing all day. Elasticsearch, like most databases, is built for data that is read often and can be changed: it indexes every term of every field as each row arrives and keeps every document updatable. Quiver is built for the cheapest possible ingest and storage instead.
- Inverted index on every term, built at ingest
- Relevance-ranked full-text search
- Data on the node's local disk
- Columnar Parquet, no per-term index
- Selective queries by service, trace and time
- Data in object storage; nodes hold no state
Smallest server for the same load, then double it
- 1
Send the same workload
50,000 records a second for 10 minutes, from 32 senders.
- 2
Shrink the compute
Quiver from 1 core / 2 GB down to half a core / 512 MB. Elasticsearch from 3 cores / 4 GB, as 1 core managed under 20,000 a second.
- 3
Keep the smallest size that keeps up
A size passes if it keeps up, doesn't crash and loses no records.
- 4
Scale up
At 100,000 a second Quiver kept up on 0.75 core; Elasticsearch would need an estimated 5 cores or more (Addendum E).
The same conditions for both
- Same AWS server type and data
- Elasticsearch on fast local disk; Quiver on slower object storage
- Writes confirmed only once safely stored
- 10-minute averages, not peaks
Full setup in Addendum F.
Quiver needs a server 6× smaller
Each at its smallest passing size: Quiver on 0.5 core / 512 MB, Elasticsearch on 3 cores / 4 GB, unless noted.
| Measure | Quiver | Elasticsearch | To scale | Quiver advantage |
|---|
Elasticsearch on the free Basic licence keeps a copy of each document (Addendum D). At 3 cores / 2 GB it reached 93.8% of the rate and failed.
What about query speed?
Query speed wasn't measured. Elasticsearch's index answers lookups very quickly, and costs CPU, memory and disk on every row ingested, including the many never queried.
Quiver skips the work Elasticsearch does on every row
Elasticsearch does its work at ingest so searches are fast later. Quiver keeps ingest light and reads only the files a query needs. Addendum G has the detail.
No search index for every word
Rows are written as columns, not indexed term by term.
Compact, compressed files
Parquet with ZSTD, and no second copy of each document.
Nothing kept on the server
No index structures held in memory; no data on local disk.
One write for many senders
Many producers share one durable commit per flush.
Use Quiver for the data AI agents work with, and Elasticsearch for text search by people
Use Quiver for
- Agent-operated telemetry, queried in bursts
- High-volume, append-only logs, events and traces
- Object storage with stateless compute
- Small footprints: edge, per-tenant, sidecar
Use Elasticsearch for
- Relevance-ranked full-text search by people
- Interactive search on any word
- Apps built on its query language and tools
The two work together: Quiver for the full stream agents work from, Elasticsearch for the subset people search by text.
Detailed test results
AEvery server size we tested at 50,000 records a second
Every cell ran 10 minutes at 50,000 rows/s. Quiver's 512 MB runs used its settings for small servers (Addendum F). Elasticsearch's smaller cells were not run: at 1 core it peaked at 19,900 rows/s, so 50,000 rows/s needs at least 2.5 cores.
| Engine | CPU cap | Memory cap | Result | Rate achieved | Engine CPU | Engine memory | Memory peak |
|---|
BHow much each database can take in on one core
Same producers with no rate limit, engine capped at 1 core / 2 GB, for 10 minutes. Elasticsearch rejected no writes; it slowed its acknowledgements instead.
| Measure | Quiver | Elasticsearch | Ratio |
|---|
CCPU and memory over the 10-minute test
Quiver at 0.5 core / 512 MB, Elasticsearch at 3 cores / 4 GB. Ten-second averages of the engine container; dashed lines are each engine's cap. Quiver's object storage (Garage) is listed separately in the table.
| Measure | Quiver | Elasticsearch |
|---|
DStorage used
Quiver stores each row in about a quarter of the space Elasticsearch needs, and needs no local disk.
Elasticsearch on the free Basic licence keeps a copy of each original document; its storage-saving synthetic source mode needs a paid licence.
Measured 2 minutes after each 10-minute load ended. Quiver: the Garage bucket. Elasticsearch: its data directory on local disk, which includes the translog. Compare storage per row: in the maximum-rate test Quiver took in more than eight times as many rows as Elasticsearch in the same 10 minutes, so its totals are larger, while each row takes far less space. Live data is what each engine reports as current.
| Measure | Quiver 50k unit 30.0M rows | Elasticsearch 50k unit 30.0M rows | Quiver max rate 103.9M rows | Elasticsearch max rate 12.1M rows |
|---|
EQuiver at twice the load (100,000 records a second)
Quiver sustained 100,000 rows/s on 0.75 core / 512 MB on the 4-vCPU machine, where every whole-machine figure below was measured. Elasticsearch was not run at this rate: at about 20,000 rows per core-second, it would need at least 5 cores.
| Measure | Quiver, 100k rows/s 0.75 core / 512 MB | Elasticsearch, 50k rows/s 3 cores / 4 GB |
|---|
Whole-machine figures include everything on the machine: the engine, its storage, the load client and the operating system.
FTest setup and versions
- Machines
- AWS us-east-1, Ubuntu 24.04, Docker. m7gd.large: 2 vCPU Graviton3, 8 GB, 118 GB local NVMe. m7gd.xlarge: 4 vCPU Graviton3, 16 GB, 237 GB local NVMe
- Quiver
- core0 build
35b4016, single node, standalone (no cache tier). Group commit at 50,000 rows or 1 s; replaced files deleted after 60 s. At 512 MB: 32 MB compaction target, 8 MB row groups, 2 concurrent flushes (4 at 100k rows/s) - Elasticsearch
- Object storage
- Garage v2.5.0, single node, for Quiver only: 1 core / 1 GB at 50k rows/s, 2 cores / 2 GB at 100k rows/s
- Data
- Synthetic log rows with 11 columns (timestamp, service, host, level, trace ID, span ID, path, status, duration, message and attributes), about 386 B per row raw. A 1M-row pool replayed with a fresh trace ID per row
- Load client
- Rust load generator, 32 concurrent producers, 5,000-row batches: Arrow Flight to Quiver, the Bulk API (NDJSON) to Elasticsearch. Client CPU under 0.1 core
- Measurement
- Container CPU and memory sampled every second from the Docker API; whole-machine CPU and memory from the operating system every second; Garage bucket size from its admin API; Elasticsearch data directory size from disk. After each run, row counts and timestamp ranges were checked against what the client saw acknowledged
GWorkload and design in detail
Logs, events and traces are written continuously and at high volume by many small producers, and they are never updated. Most telemetry is written once and never read.
Elasticsearch is a search engine, designed so that searches are fast. It pays for that speed at ingest: it indexes every term of every field as each row arrives, so that a person can type anything into a search box and get ranked results back. That work is done for every row, whether or not anyone ever searches it.
In FFWD, telemetry is operated by agents. Agents filter by service, trace ID and time window, follow the lead to the next query, and do it in bursts when something goes wrong. They rarely need relevance-ranked full-text search. That workload rewards the cheapest possible ingest and storage. Quiver is designed from that opposite starting point: fast ingest first, with the work of finding data done at query time, and only for the data an agent asks about.
The two databases start from opposite ends. Elasticsearch does its work at ingest so that searches are fast later. Quiver keeps ingest light and does its work at query time, reading only the files a query needs.
Elasticsearch turns every incoming row into a Lucene document. It parses the JSON, builds an inverted index for every term, writes column values for aggregations, keeps a copy of the original document, and merges index segments continuously in the background. Quiver is written in Rust on Apache Arrow. Rows arrive over Arrow Flight already in columnar form, concurrent writes are group-committed into one compressed Parquet file per flush, and no per-term index is built. Queries skip irrelevant files using per-file statistics, a clustering key and bloom filters on trace IDs, read at query time. Nodes keep no state, so the data lives only in object storage.
This comparison covers ingest and storage; query speed was not measured on the test machines. Elasticsearch's inverted index answers a lookup such as a single trace ID very quickly. That same index is what costs it CPU, memory and disk on every row it ingests, including the large majority that are never queried.