Product · per-query targets · checked before you ship

Set the number. Then find the table that holds it.

Every query gets a target: how fast it comes back, how many rows it returns, how fresh they have to be. Kijun builds the table at the scale you're heading for, then diagnoses every missed target and re-lays the table out to fix its cause, until every target holds, under query latency and sustained write load. What comes back is a readable report of which layout won, what it cost, and the trade-offs. Apache Iceberg today, more engines next.

/01 · THE RUNBEFORE YOU SHIP · METERED

The benchmark run

The layout is how the target gets met. It is not the thing you were promised. Run the queries you're about to ship against data built to your own profile, not your staging snapshot and not TPC-DS, and Kijun diagnoses each miss and reworks the layout until every query is inside its own target.

  1. Bring the table, your queries, and your targets.
    Already have the table? Upload its metadata as a zip and Kijun reads your column types, cardinalities, null rates, ranges, row counts and arrival rate straight out of it. Building something new? Describe it instead. Either way you then say what each query needs: how fast it must return, how many rows it should return, how fresh the data has to be, and the scale you're growing into.
  2. Kijun builds the table at production scale.
    Synthetic data built to the profile you gave it, including the shape of your heavy accounts against the long tail. Materialized as real Parquet and real Iceberg metadata on Kijun's infrastructure. No copy of your data, because none is needed.
  3. We run your workload across candidate layouts.
    Every query you defined, measured against the target you gave it, on every candidate. Two tests per layout: query latency, and sustained commit load, where the write test ramps from a starting rate until commits begin to conflict, so what you get back is the ceiling rather than a pass mark. What changed between one candidate and the next is Kijun's problem, not yours. Measured on AWS Athena, on Kijun's own infrastructure. Nothing is read from or written to your storage.
  4. You get a readable report: which layout won, what it cost, the trade-offs.
    Including the maintenance the winner was measured under: the compaction schedule the result depends on, what it cost during the run, and a monthly projection with the arithmetic shown. Metered, not estimated. Export the winning layout as ready-to-run Iceberg DDL and the results as CSV or JSON. The layout is table metadata, so it goes with the table to whatever reads it next: Trino, Spark, Flink.
  5. Then check the answer yourself.
    The tables Kijun measured are real and they are still there when the run ends. Open the playground and query them: the winner, the ones that lost, or the winner grown to next year's volume. Read-only, same engine, same tables.
benchmark run · orders · 2B rows · 12 queries
RUN #14 · 4 IT.
STEP 01Build
2.0B rows · 18 cols · 11min
STEP 02Iterate
candidate · measure · repeat
STEP 03Report
winning layout · export
RUN
LAYOUT
SLOWEST
MET TARGET
VERDICT
iter_01
partition_v1 · sort: id · target: 256MB
4,240ms
5 of 12
FAIL
iter_02
partition_v2 · sort: ts,id · target: 256MB
2,120ms
9 of 12
FAIL
iter_03
partition_v3 · sort: ts,id · target: 256MB
1,380ms
12 of 12
PASS
iter_04
partition_v3 · sort: cid,pid · 128MB
1,120ms
12 of 12
PASS · ★
Example report
/02 · the dataBelievable data
Why measuring on synthetic data is valid: it reproduces your statistics, not your rows.

Synthetic data, with your distributions.

A query plan is shaped by data statistics, not data itself. The cardinality of a join key, the skew of a partition, the correlation between two columns: these are what the optimizer reads when it picks a plan. Copy production and you copy PII. Generate from a schema alone and you get TPC-DS: synthetic data with synthetic statistics, which tells you nothing.

So Kijun builds from the profile you gave in step 01, whether it was read out of uploaded metadata or declared in the UI. A few megabytes of shape go in; hundreds of gigabytes of data come out, built on Kijun's own infrastructure. No row, no value, no identifier of yours is involved. This asymmetry is the whole point.

FUTURE SCALEThe synthesizer doesn't stop at today's scale. Give it a growth pattern, "2B rows by next March, 5,000 commits/min sustained", and it physically constructs that projected table: real Parquet, real Iceberg metadata, at the row count you're heading toward. Every benchmark run then executes against the future table. Measured points, not extrapolation.
CARDINALITY
Per-column distinct counts
Read from your metadata when you upload it, declared when you don't. Exact reproduction at the target either way. We never round.
SKEW
How rows spread across accounts
A fleet of sensors spreads evenly. A marketplace where twelve sellers do half the volume does not. You say which, and the heavy tail is fitted rather than flattened.
CORRELATION
Pairwise column dependency
The dependencies you declare between column pairs are reproduced in the generated data, not flattened away. The plan optimizer reads them.
TIME
Temporal shape
Seasonality, burstiness, churn. We reproduce arrival rate, not just count.

What we don't claim

Kijun is not a privacy-preserving DB clone. We don't pretend a synthetic row "is" a real row with a hat on. Differential privacy is a red herring here, because we never emit anything per-row, only aggregate distributions. If you need to test exact PII flows, you need anonymization, not synthesis. Different problem.

/03 · the loopDiagnosis, not brute force
Measure, find the cause, fix it, measure again. On record, round by round.

Every round exists to fix a diagnosed miss.

Iceberg's layout space runs to millions of points, and the dimensions interact: a change that helps one query can hurt another. Brute force is not a strategy; it's a budget.

So Kijun doesn't guess the next layout. After each round it works out, per failing query, what in the layout caused the miss, and the next candidate exists to remove that cause. The worst misses get fixed first, a fix that was already tried is never tried twice, and a layout that fixes one query by breaking another does not survive the next round.

Example · the round history a run records
ROUND
CONFIG
QUERIES MISSING TARGET
WORST MISS
round_0
cfg_0
7 of 12
+183%
round_1
cfg_1
3 of 12
+41%
round_2
cfg_2
1 of 12
+9%
round_3
cfg_3
0 of 12
MET_SLA ✓

The loop is capped at four rounds. When every query holds its target it stops early, and the verdict is MET_SLA. When the cap arrives first, you get the best measured layout with the gap on record: the verdict says BEST_AVAILABLE, never a silent pass. Either way the round history is stored on the run and you can read it back: which layout each round measured, what changed between rounds, and what that did to every query.

/04 · the exportsWhat you take away
The report you read, and the files you export. Nothing recomputed, nothing estimated.

First a readable report. Then three exports.

What you get when a run finishes is a clean report in the UI: which layout won, what it cost, and the trade-offs across query latency and write load. That's the deliverable you act on. On top of it, three exports, every one read back from the finished run. Nothing is recomputed, re-run or estimated at export time.

Iceberg DDL. The winning configuration as ready-to-run CREATE TABLE statements, one per table plus a combined .sql. A run that never settled on a winner has no DDL export: Kijun refuses to hand you a table nobody chose.

Results as JSON. The exact payload the Results screen reads, so the file and the screen can never disagree. Abridged:

Example · results.json · abridged
{
  "status":               "COMPLETED",
  "winningConfigName":    "cfg_3",
  "winningConfigVerdict": "MET_SLA",
  "params": {
    "tableCount": 3,  "configurationsTested": 4,
    "totalRows": 2_104_886_412,  "writeMeasurementEnabled": true
  },
  "perTable": [{
    "tableName":   "orders",
    "layoutLabel": "day(event_ts) · bucket(16, customer_id)",
    "writeVerdict": { "ceilingObserved": true, "ceilingMultiplier": 2.5,
                      "monthsUntilCeiling": 14 }
  }, …],
  "slaCompliance": [{
    "queryName": "orders_by_customer",  "targetLatencyMs": 1500,
    "actualTotalTimeMs": 1120,  "bytesScanned": 18_412_986_368,
    "meetsSla": true,  "outcomeKind": "PASS"
  }, …],
  "maintenance": {
    "projectedMonthlyCost": { "measuredCostPerRunUsd": 0.42,
      "runsPerMonth": 96, "monthlyCostUsd": 40.32, "monthDays": 30 }
  }
}

Results as CSV. Two sections: one row per table (layout, rows, bytes, files, partitions) and one row per query (target, measured latency, bytes scanned, verdict). An unmeasured value is an empty cell, never a zero: zero is a measurement, and these are not.

On the roadmap (not built yet): a signed, content-addressed baseline built from these exports, for your monitoring stack and CI/CD pipeline to consume as ground truth.

/05 · the boundaryWhere your data sits
What crosses the boundary, and what never does.

Your data never moves.

A benchmark run executes end to end on Kijun's own infrastructure. Nothing of Kijun's runs in your environment: there is nothing to install, and no connection to open. The only thing that crosses the boundary is the profile, a few megabytes of shape. Rows, values and identifiers never do, and nothing is read from your storage or written to it.

THE BOUNDARY · what crosses, what never does
YOUR ENVIRONMENTKIJUN'S ENVIRONMENTyour production clusteryour tables, your rowsbuilds the synthetic tableruns your queriesKijun's S3 · AWS Athenanothing installedno connection openevery table the run buildsstays here, and only herethe profilefield names · query textstatistics · a few MBrows · values · identifiersnever cross

What's shipped

FEATMetadata import. Upload a zip of your table's metadata and Kijun reads the profile out of it, measured-or-absent, never defaulted.
FEATWrite-load scoring. Candidate layouts measured under sustained commit load, not just reads.
FEATPlayground. Query the tables a run built, including the winner grown to a future volume. Read-only, scan-capped.

On the roadmap, not built yet

Everything in this list is unbuilt. None of it is available today, at any price, in any plan.

PLANNEDMongoDB. The next intended database engine after Apache Iceberg.
PLANNEDIn-network extractor. Reaches into your infrastructure and samples statistics directly, instead of you uploading metadata or describing the table.
PLANNEDSelf-hosted runner. Runs the build-and-measure work inside your own VPC instead of on Kijun's infrastructure.
PLANNEDPriors across runs. A registry of past runs, so a run can start warm on a schema family already seen. Today each loop runs strictly within a single run.
PLANNEDSigned, content-addressed baseline artifact. Exportable and verifiable as ground truth.
PLANNEDMonitor. A separate product that would read tables you explicitly connect to it, watching them against a published baseline. Described only; nothing on this page depends on it.
PLANNEDDownstream consumption. Baselines read by CI checks and monitoring stacks as ground truth.
PLANNEDSOC 2 Type II. Planned. No observation window has started.
/06 · after the run · on the roadmap

What the run leaves behind.

Today's deliverable is the report and its exports: the measured run, the winning layout, the maintenance plan it was measured under, and ready-to-run Iceberg DDL. Dated, reproducible, and yours to keep. A signed, content-addressed baseline you can diff against later is on the roadmap.

On the roadmap (not built yet): your observability stack and CI/CD pipeline consume that baseline as ground truth, so a regression surfaces as a verifiable diff. The same workflow you run for unit tests, but for database performance.

See pricing →
KIJUN · TODAY
benchmark run
report · winning layout · DDL
→
PRODUCTION
deploy
orders.compaction_v4
→
ROADMAP
compare vs baseline
Datadog · Grafana · CI
↻   re-run Kijun after any platform upgrade to refresh the reportsigned baseline · on the roadmap
/07 · start

Deploy knowing. Not hoping.

Request early access
private beta · Apache Iceberg today
Benchmark testing for databases. Measure the change before you ship it. Apache Iceberg today.Apache Iceberg engine: ready© 2026 Kijun

Product

How it worksProductPricingDocs

Platform

Apache IcebergRoadmap

Resources

DocumentationChangelog

Company

ContactPrivacyTermsCookie settings