For agent-operated telemetry, Quiver needs 5× less CPU, 10× less memory and 4× less storage than Elasticsearch®

In our tests, Quiver took in the same log stream on a server 6× smaller than Elasticsearch needed, with nothing on local disk.

Elasticsearch is built for fast search, doing the heavy work as each record arrives. Quiver starts from the opposite end: fast, cheap ingest, because FFWD's telemetry is read by AI agents in short bursts.

QuiverElasticsearch 9.5Quiver on Garage object storage; Elasticsearch on local NVMe
4.8×
less CPU
10×
less memory
3.7×
less storage
What it costs

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.

Server to provision
6× smaller0.5 vs 3 cores
Smallest size at 50,000 records/s; 512 MB vs 4 GB memory.
Compute in use
4.8× less0.53 vs 2.54 cores
Quiver's figure includes its object storage.
Storage per record
3.8× less103 vs 390 B
On object storage, nothing on local disk.
Background

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.

Built for fast search
Elasticsearch
  • Inverted index on every term, built at ingest
  • Relevance-ranked full-text search
  • Data on the node's local disk
Built for fast ingest
Quiver
  • Columnar Parquet, no per-term index
  • Selective queries by service, trace and time
  • Data in object storage; nodes hold no state
How we tested

Smallest server for the same load, then double it

  1. 1

    Send the same workload

    50,000 records a second for 10 minutes, from 32 senders.

  2. 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. 3

    Keep the smallest size that keeps up

    A size passes if it keeps up, doesn't crash and loses no records.

  4. 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.

Results

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.

MeasureQuiverElasticsearchTo scaleQuiver 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.

Why Quiver is more efficient

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.

When to use which

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.

Addenda

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.

EngineCPU capMemory capResultRate achievedEngine CPUEngine memoryMemory 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.

MeasureQuiverElasticsearchRatio

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.

MeasureQuiverElasticsearch

DStorage used

Quiver stores each row in about a quarter of the space Elasticsearch needs, and needs no local disk.

Bytes per row at 50k rows/s
3.8× less103 vs 390 B
After 30M rows each
Bytes per row at maximum rate
3.5× less119 vs 414 B
After 104M vs 12M rows
Local disk on the engine node
0 Bvs 10.9 GiB
Quiver keeps all data in object storage

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.

MeasureQuiver
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.

MeasureQuiver, 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.