KOVA
Docs

Benchmark

Five passes against a server that is already running. Each pass rebuilds the database benchmark. The page shows the mean time.

Run it

Start the listener first. Security stays off, which is the default. Then:

make benchmark

The port defaults to 27018. BENCH_ROWS defaults to 1000 orders. BENCH_LOOKUPS defaults to 100 point reads. The script then asks whether to repeat the same steps on a MongoDB already listening on port 27017. Answering no runs Kova only. Answering yes drops and recreates only the database named benchmark on that server. BENCH_MONGO=1 answers yes. MONGO_PORT chooses the MongoDB port.

make benchmark KOVA_PORT=27019 BENCH_ROWS=1000 BENCH_LOOKUPS=100

The run creates the database benchmark. It does not need that database to exist already. If a previous run left one, that database is removed first. Other databases stay. A release binary is the number worth keeping. Stop the listener that is already up, then start that binary with make run-release. make release only builds it. A debug binary still runs, and the page names which binary answered.

What it measures

Each order is sku, dept, job, sal, qty, and note. job cycles through clerk, analyst, manager, and salesman. note contains that word, so a text search for clerk hits a quarter of the rows. bonus is set on every 10th order. Four departments sit in dept. After the indexes exist, sku 0 is deleted so the point lookups start at 1.

MQL inserts the rows, builds a unique index, a range index, a hash index, a compound index, and a sparse index, then updates, deletes, numbers a field, commits one transaction, and aborts another. It reads by index, scans an unindexed field, groups, looks up dept, writes high_pay with $out, and searches the text view orders_search, including a misspelling.

SQL reads those same rows: a point lookup, a range, a count, a group, an inner join, text search, a fuzzy match, and groups. CREATE TABLE, INSERT, UPDATE, and DELETE are sent as SQL and written to the pages. One update and one delete change a single row. Another update and another delete match every order whose qty is 7.

The second point-lookup pass runs after the first pass has already read the pages. Index, scan, hit, miss, and WAL are the change in serverStatus during that step. Hit, miss, and WAL are Kova only.

How the times are compared

The benchmark runs the whole pass five times. BENCH_REPEATS changes that number. Each pass drops and recreates the database benchmark. The time in the table is the mean of those passes. Rows, index, scan, hit, miss, and WAL are means too. The smaller line under a time is the sample standard deviation: the square root of the sum of squared distances from the mean, after that sum is divided by one less than the number of passes.

When MongoDB ran the same step, each difference is Kova minus Mongo on that pass. If the absolute value of the mean difference is less than or equal to the standard deviation of those differences, the step is similar. A similar step is not a win for either side, and the row is not tinted. A tinted row means Kova's mean was faster by more than that standard deviation. The line under the MQL table counts Kova's wins, MongoDB's wins, the similar steps, and the steps that could not be compared because MongoDB does not have that feature. Those last steps are listed at the end of the MQL table. The SQL table lists Kova only.

ColumnWhat the number is
OperationThe name of the step. The button opens the command that ran.
Resultok means the server finished the step. fail means it returned an error.
RowsDocuments inserted, changed, deleted, or returned.
TimeMean wall-clock time of the passes, from send to reply. The smaller line is the sample standard deviation. When MongoDB ran, the MQL cell names Kova and Mongo. The last line is Kova minus Mongo, then Kova's mean divided by Mongo's, or the word similar when that difference stays within the standard deviation of the paired differences. Neither side wins a similar step. An oxide mark and a tinted row mean Kova was faster by more than that. The count under the MQL table is Kova's wins, MongoDB's wins, the similar steps, and the steps MongoDB does not have. The SQL table lists Kova only.
Rows/sRows divided by the time. A step with no rows has no rate.
IndexKova is indexLookups: how many times the step chose an index. One range query is 1, however many documents come back. Mongo is keys examined: how many index keys the step walked. A range over 675 keys is 675. The two numbers count different events. N/A means not available.
ScanHow many times the step started a collection scan, on Kova and on Mongo. One find that walks the collection is 1. The pages that walk reads are under Hit.
HitKova only. Buffer-pool pages served from memory during the step. Reading a collection once counts one hit per page, so Scan can be 1 while Hit is 77.
MissKova only. Buffer-pool pages read from disk because they were not cached.
WALKova only. Log records appended by the step. Many documents can share one record.
NoteExtra detail for the step.

This run

FigureValue
When 28 Oct 2026, 09:49:47 CET
Address 127.0.0.1:27018
MongoDB 8.0.19 at 127.0.0.1:27017
Process /home/marcelo/Documents/git/kova/target/release/kova (release)
Workload 1,000 orders, 100 point lookups. The run creates the database benchmark.
Operating system Ubuntu 24.04.4 LTS, Linux 6.8.0-139-generic x86_64
RAM 31.2 GiB
Disk 286.3 GiB free of 915.3 GiB
Video memory NVIDIA GeForce RTX 3060, 12.0 GiB

The inserted documents are benchmark-data.json. The timings and the process figures are benchmark-result.json.

Process memory and disk

FigureThis process
Memory in use at the end 373.9 MiB
Memory growth during the run 115.5 MiB
Memory high-water 373.9 MiB
Bytes read 0 B
Bytes written 3.8 MiB

Times

This run repeated each step 5 times. Each pass drops and recreates the database benchmark. The time is the mean of those passes. The smaller line is the sample standard deviation: the square root of the sum of squared distances from the mean, after that sum is divided by one less than the number of passes. A step is similar when MongoDB also finished it and the absolute difference of the two means is less than or equal to the standard deviation of the paired differences, Kova minus Mongo on the same pass. A similar step is not a win. A tinted row is a step Kova won by more than that.

Each paired cell names Kova and Mongo. The last line is Kova minus Mongo in milliseconds, then how many times Kova's mean is Mongo's. An oxide mark sits beside the faster time. The whole row is tinted when Kova was faster by more than the standard deviation. The last line says similar when the difference of the means stayed within the standard deviation of the paired differences, and neither side wins. Under the MQL table, the count is how many steps Kova won, how many MongoDB won, how many were similar, and how many could not be compared because MongoDB does not have that feature. Those steps are listed last. The SQL table lists Kova only. N/A means not available: MongoDB did not run that step, or did not report that counter. Index on Kova is how many times the step chose an index. One range is 1. Index on Mongo is how many index keys the step walked. Scan is how many collection scans the step started, and one walk of the collection is 1. Hit is Kova only: pages read from the buffer pool. A scan of 1 can show many hits, one per page.

MQL

Operation Result Rows Time Rows/s Index Scan Hit Miss WAL Note
ok1Kova44.8 ms± 9 msMongo71.6 ms± 23.2 ms-27 ms, 0.6×22Kova0Mongo0Kova0Mongo02204Removed a previous benchmark database.
ok3Kova17.2 ms± 0.4 msMongo100.2 ms± 2.5 ms-83 ms, 0.2×174Kova0Mongo0Kova0Mongo0003Creates dept, orders, and ledger. Nothing is assumed to exist.
ok4Kova12.2 ms± 1.1 msMongo16.4 ms± 3.7 ms-4 ms, 0.7×328Kova0Mongo0Kova0Mongo0001
ok1,000Kova108.4 ms± 6.7 msMongo71.4 ms± 5.3 ms+37 ms, 1.5×9,225Kova0Mongo0Kova0Mongo0102
ok1,000Kova39.4 ms± 2.2 msMongo104.2 ms± 2.2 ms-65 ms, 0.4×25,381Kova0Mongo0Kova0Mongo01601
ok1,000Kova41.2 ms± 2.3 msMongo99 ms± 0 ms-58 ms, 0.4×24,272Kova0Mongo0Kova0Mongo01601
ok1,000Kova354.6 ms± 696.2 msMongo101.4 ms± 3.2 mssimilar2,820Kova0Mongo0Kova0Mongo01601
ok1,000Kova46.4 ms± 4.4 msMongo100 ms± 0.7 ms-54 ms, 0.5×21,552Kova0Mongo0Kova0Mongo01601
ok1,000Kova44.6 ms± 6.6 msMongo96.6 ms± 2.3 ms-52 ms, 0.5×22,422Kova0Mongo0Kova0Mongo01601
ok1,000Kova92 ms± 6.3 msMongo82 ms± 13.7 mssimilar10,870Kova0Mongo0Kova0Mongo11601
ok1Kova13.8 ms± 0.8 msMongo8 ms± 1.4 ms+5 ms, 1.7×72Kova0Mongo1Kova0Mongo01601
ok1Kova12.4 ms± 0.5 msMongo4.4 ms± 0.9 ms+8 ms, 2.8×81Kova0Mongo1Kova0Mongo01601
ok1Kova14.8 ms± 0.4 msMongo12.6 ms± 1.5 ms+2 ms, 1.2×68Kova0Mongo1Kova0Mongo01601
ok999Kova5.2 ms± 0.4 msMongo2.4 ms± 0.9 ms+3 ms, 2.2×192,115Kova0Mongo0Kova1Mongo01500
ok50Kova9.4 ms± 0.5 msMongo38.6 ms± 2.9 ms-29 ms, 0.2×5,319Kova0Mongo0Kova0Mongo0001
ok1Kova6.6 ms± 0.5 msMongo36.2 ms± 1.9 ms-30 ms, 0.2×152Kova0Mongo0Kova0Mongo0001
ok100Kova313.8 ms± 17.6 msMongo185.2 ms± 11.1 ms+128 ms, 1.7×319Kova100Mongo100Kova0Mongo010000100 findOne by sku
ok100Kova289.2 ms± 24.7 msMongo134.2 ms± 11.8 ms+155 ms, 2.2×346Kova100Mongo100Kova0Mongo010000100 findOne by sku
ok675Kova20.8 ms± 2.3 msMongo8.6 ms± 1.1 ms+12 ms, 2.4×32,452Kova1Mongo675Kova0Mongo01400
ok20Kova6 ms± 0.7 msMongo2.2 ms± 0.4 ms+4 ms, 2.7×3,333Kova0Mongo0Kova1Mongo11500qty has no index
ok4Kova12 ms± 0.7 msMongo4.8 ms± 0.4 ms+8 ms, 2.5×333Kova0Mongo0Kova1Mongo11500
ok100Kova8.2 ms± 0.8 msMongo4.8 ms± 0.8 ms+4 ms, 1.7×12,195Kova2Mongo100Kova1Mongo1300
ok675Kova51.4 ms± 3.4 msMongo50.2 ms± 3 mssimilar13,132Kova1Mongo675Kova1Mongo01702
ok8Kova3 ms± 0 msMongo3.2 ms± 0.4 mssimilar2,667Kova0Mongo0Kova0Mongo0000
ok1Kova6.2 ms± 0.4 msMongoN/A161Kova0MongoN/AKova0MongoN/A001
ok2Kova10.8 ms± 0.4 msMongoN/A185Kova0MongoN/AKova0MongoN/A101
ok1Kova5.4 ms± 0.5 msMongoN/A185Kova0MongoN/AKova0MongoN/A100
ok1,000Kova163.6 ms± 3.3 msMongoN/A6,112Kova0MongoN/AKova0MongoN/A1501
ok250Kova13.8 ms± 2.3 msMongoN/A18,116Kova1MongoN/AKova0MongoN/A1500
ok100Kova86.2 ms± 6.1 msMongoN/A1,160Kova0MongoN/AKova0MongoN/A10000
ok100Kova72.8 ms± 1.3 msMongoN/A1,374Kova0MongoN/AKova0MongoN/A10000

Kova was faster in 9 steps. MongoDB was faster in 11 steps. 4 steps were similar: the difference stayed within the standard deviation. 7 steps could not be compared, because MongoDB does not have that feature.

SQL

Operation Result Rows Time Rows/s Index Scan Hit Miss WAL Note
ok13.6 ms± 0.5 ms27800000
ok100525.8 ms± 12.7 ms190100010000100 statements, WHERE sku = n
ok100536.8 ms± 21.5 ms186100010000100 statements, WHERE sku = n
ok67524.8 ms± 0.8 ms27,218101400
ok422.6 ms± 4.2 ms177011500
ok119 ms± 3.7 ms53011500
ok99923.8 ms± 1.6 ms41,975021600
ok10096.4 ms± 9.2 ms1,0370010000
ok10074.2 ms± 4.1 ms1,3480010000
ok581 ms± 3.4 ms620010000
ok71.6 ms± 0.5 ms4,37500000
ok71.6 ms± 0.5 ms4,37500000
ok67516.6 ms± 2.9 ms40,663101400
ok6759.8 ms± 0.8 ms68,87801300
ok06.2 ms± 0.4 ms000001
ok15.8 ms± 0.4 ms17200001
ok1106.2 ms± 3.2 ms9003001
ok20126.6 ms± 5.3 ms158003001
ok16.2 ms± 0.4 ms16100201
ok2058.4 ms± 2.5 ms342003001

An operation name opens the command. A question mark explains that column. A second point lookup runs after the first has already read the pages.