Data Systems / 2026-05-21
Materialized View API Serving Benchmark
This API-level benchmark tested whether StarRocks async materialized views could replace denormalized RDS tables for low-latency REST response shapes. The work covered 100k, 1m, and 10m row scales with exact payload parity checks.
100k, 1m, and 10m rows
Overview, ranked lists, filters, snapshots
No failed benchmark calls
Canonical responses matched
Validate whether async materialized views could serve existing REST response shapes without sacrificing latency or payload correctness.
Benchmarked eight API endpoints across 100k, 1m, and 10m row scales with canonical payload parity checks at every scale.
The StarRocks path was correct, but RDS remained faster for the tested low-latency REST shape; ingestion behavior remained a caveat.
- PostgreSQL
- StarRocks
- Iceberg
- S3 Tables
- Materialized Views
- REST APIs
- Docker
Executive readout
RDS remained the faster serving path for this specific REST API shape. At 10 million rows, sequential RDS p50 stayed between 0.91 ms and 14.40 ms across the eight endpoints, while StarRocks async materialized views ranged from 13.40 ms to 175 ms.
The recommendation was not to assume StarRocks async materialized views can replace RDS for low-latency REST serving without additional architecture work, even though the StarRocks path was functional and correct at 10 million rows.
What was compared
- RDS path: temporary denormalized Postgres tables for snapshot data, filter options, and ranked result cards, with B-tree and GIN indexes.
- StarRocks path: Iceberg base tables in AWS S3 Tables plus async materialized views using REFRESH DEFERRED MANUAL.
- Fairness gate: every endpoint response was canonicalized and compared at every scale.
10M sequential latency
| Endpoint | RDS p50 | StarRocks MV p50 | MV/RDS ratio |
|---|---|---|---|
| Overview cards rank 50 | 4.04 ms | 50.05 ms | 12.39x |
| Ranked items top 50 | 3.35 ms | 49.75 ms | 14.85x |
| Activity/day top 200 | 6.56 ms | 56.78 ms | 8.66x |
| High-velocity items top 50 | 2.93 ms | 53.30 ms | 18.19x |
| Multi-filter items top 50 | 14.40 ms | 175 ms | 12.16x |
| Search launch items top 50 | 8.22 ms | 42.34 ms | 5.15x |
| Filter options | 1.65 ms | 23.19 ms | 14.05x |
| Snapshot metadata | 0.91 ms | 13.40 ms | 14.73x |
Setup and refresh costs
| Scale | RDS total | StarRocks total |
|---|---|---|
| 100k rows | 4.2 s | 29.5 s |
| 1m rows | 30.4 s | 46.4 s |
| 10m rows | 335.7 s | 110.8 s |
StarRocks and S3 Tables caveat
Repeated appends into the same AWS S3 Tables Iceberg table failed from StarRocks with PreconditionFailed after the first append. A single large insert worked, but the exact append and commit behavior needs to be resolved before production ingestion can depend on this path.
- Successful 10m run used reduced parallelism settings including enable_adaptive_sink_dop=false and pipeline_sink_dop=1.
- The materialized view path can serve equivalent payloads, but ingestion behavior remains an architecture risk.
Detailed methodology and results
Supporting methodology, figures, and tables are rendered here as native page content with the same visual system as the rest of this website.
API-level benchmark using denormalized benchmark tables that mirror the REST API response shapes. Results were generated from the new 100k, 1m, and 10m runs saved in the benchmark result JSON.
Executive Readout
RDS remains the faster serving path for this API shape. At 10m rows, sequential RDS p50 latency stayed between 0.91ms and 14.40ms across the eight endpoints. StarRocks async MVs stayed between 13.40ms and 175ms .
The slowest relative 10m sequential endpoint was Content items, high-velocity tab, 50 rows , where StarRocks MV p50 was 18.19x RDS p50. Under 100-way concurrency, the category/country/language endpoint was the biggest gap: RDS p50 168ms versus StarRocks MV p50 2.21s .
Recommendation:
What Was Compared
RDS Path
- Temporary denormalized PostgreSQL tables for snapshot, filters, and result cards.
- B-tree indexes for sorted/filtered lookup paths plus GIN indexes for array/search fields.
- API call executed through the benchmark HTTP service against RDS.
StarRocks MV Path
- Iceberg base tables in AWS S3 Tables.
- StarRocks asynchronous materialized views with REFRESH DEFERRED MANUAL .
- Separate sorted content MVs for rank, activity/day, velocity, views, and published-at API sorts.
- API call executed through the same benchmark HTTP service against StarRocks MVs.
Fairness check:
Payload Parity
| Scale | Rows | Parity | Note |
|---|---|---|---|
| 100k | 100,000 | 8/8 matched | All canonicalized response payloads matched exactly |
| 1m | 1,000,000 | 8/8 matched | All canonicalized response payloads matched exactly |
| 10m | 10,000,000 | 8/8 matched | All canonicalized response payloads matched exactly |
Sequential p50 Latency by Scale
These are single-client API calls after warmups. Ratios show StarRocks MV p50 divided by RDS p50.
| Scale | Endpoint | RDS p50 | StarRocks MV p50 | MV / RDS |
|---|---|---|---|---|
| 100k | Overview cards, rank sort, 50 rows | 6.78ms | 46.83ms | 6.91x |
| 100k | Content items, rank sort, 50 rows | 2.81ms | 46.96ms | 16.71x |
| 100k | Content items, activity/day sort, 200 rows | 9.08ms | 54.17ms | 5.97x |
| 100k | Content items, high-velocity tab, 50 rows | 4.77ms | 48.44ms | 10.16x |
| 100k | Content items, brand + country + language, 50 rows | 13.15ms | 37.57ms | 2.86x |
| 100k | Content items, search launch, 50 rows | 3.50ms | 32.88ms | 9.39x |
| 100k | Filter options | 2.06ms | 22.00ms | 10.68x |
| 100k | Snapshot metadata | 1.17ms | 11.79ms | 10.08x |
| 1m | Overview cards, rank sort, 50 rows | 3.25ms | 48.67ms | 14.98x |
| 1m | Content items, rank sort, 50 rows | 2.91ms | 48.43ms | 16.64x |
| 1m | Content items, activity/day sort, 200 rows | 7.09ms | 55.04ms | 7.76x |
| 1m | Content items, high-velocity tab, 50 rows | 2.95ms | 49.69ms | 16.84x |
| 1m | Content items, brand + country + language, 50 rows | 3.69ms | 78.66ms | 21.32x |
| 1m | Content items, search launch, 50 rows | 3.21ms | 37.33ms | 11.63x |
| 1m | Filter options | 1.70ms | 21.07ms | 12.39x |
| 1m | Snapshot metadata | 1.16ms | 12.00ms | 10.34x |
| 10m | Overview cards, rank sort, 50 rows | 4.04ms | 50.05ms | 12.39x |
| 10m | Content items, rank sort, 50 rows | 3.35ms | 49.75ms | 14.85x |
| 10m | Content items, activity/day sort, 200 rows | 6.56ms | 56.78ms | 8.66x |
| 10m | Content items, high-velocity tab, 50 rows | 2.93ms | 53.30ms | 18.19x |
| 10m | Content items, brand + country + language, 50 rows | 14.40ms | 175ms | 12.16x |
| 10m | Content items, search launch, 50 rows | 8.22ms | 42.34ms | 5.15x |
| 10m | Filter options | 1.65ms | 23.19ms | 14.05x |
| 10m | Snapshot metadata | 0.91ms | 13.40ms | 14.73x |
10m Concurrency Results
Concurrency levels were sequential, 5, 25, and 100. For concurrent runs, each worker issued 5 requests.
| Endpoint | Concurrency | RDS p50 | RDS p95 | MV p50 | MV p95 | MV / RDS p50 |
|---|---|---|---|---|---|---|
| Overview cards, rank sort, 50 rows | 1 | 4.04ms | 18.21ms | 50.05ms | 55.84ms | 12.39x |
| Overview cards, rank sort, 50 rows | 5 | 31.13ms | 108ms | 59.44ms | 73.31ms | 1.91x |
| Overview cards, rank sort, 50 rows | 25 | 61.69ms | 612ms | 189ms | 251ms | 3.06x |
| Overview cards, rank sort, 50 rows | 100 | 237ms | 522ms | 673ms | 1.20s | 2.84x |
| Content items, rank sort, 50 rows | 1 | 3.35ms | 31.42ms | 49.75ms | 52.91ms | 14.85x |
| Content items, rank sort, 50 rows | 5 | 9.41ms | 13.75ms | 65.42ms | 98.96ms | 6.95x |
| Content items, rank sort, 50 rows | 25 | 44.33ms | 54.72ms | 237ms | 260ms | 5.35x |
| Content items, rank sort, 50 rows | 100 | 174ms | 366ms | 716ms | 1.04s | 4.11x |
| Content items, activity/day sort, 200 rows | 1 | 6.56ms | 23.38ms | 56.78ms | 61.19ms | 8.66x |
| Content items, activity/day sort, 200 rows | 5 | 26.44ms | 38.04ms | 92.59ms | 106ms | 3.50x |
| Content items, activity/day sort, 200 rows | 25 | 123ms | 155ms | 386ms | 411ms | 3.14x |
| Content items, activity/day sort, 200 rows | 100 | 459ms | 1.03s | 862ms | 1.93s | 1.88x |
| Content items, high-velocity tab, 50 rows | 1 | 2.93ms | 26.12ms | 53.30ms | 81.39ms | 18.19x |
| Content items, high-velocity tab, 50 rows | 5 | 9.91ms | 23.39ms | 62.60ms | 87.43ms | 6.32x |
| Content items, high-velocity tab, 50 rows | 25 | 48.63ms | 73.13ms | 241ms | 260ms | 4.96x |
| Content items, high-velocity tab, 50 rows | 100 | 169ms | 348ms | 901ms | 1.38s | 5.34x |
| Content items, brand + country + language, 50 rows | 1 | 14.40ms | 31.97ms | 175ms | 349ms | 12.16x |
| Content items, brand + country + language, 50 rows | 5 | 10.76ms | 17.40ms | 201ms | 390ms | 18.67x |
| Content items, brand + country + language, 50 rows | 25 | 47.61ms | 69.38ms | 460ms | 503ms | 9.66x |
| Content items, brand + country + language, 50 rows | 100 | 168ms | 392ms | 2.21s | 3.07s | 13.12x |
| Content items, search launch, 50 rows | 1 | 8.22ms | 23.18ms | 42.34ms | 227ms | 5.15x |
| Content items, search launch, 50 rows | 5 | 10.09ms | 20.60ms | 80.11ms | 263ms | 7.94x |
| Content items, search launch, 50 rows | 25 | 48.11ms | 59.50ms | 141ms | 195ms | 2.94x |
| Content items, search launch, 50 rows | 100 | 157ms | 401ms | 468ms | 827ms | 2.98x |
| Filter options | 1 | 1.65ms | 3.42ms | 23.19ms | 100ms | 14.05x |
| Filter options | 5 | 9.94ms | 16.13ms | 24.16ms | 36.04ms | 2.43x |
| Filter options | 25 | 25.53ms | 37.33ms | 45.55ms | 110ms | 1.78x |
| Filter options | 100 | 76.92ms | 167ms | 265ms | 489ms | 3.44x |
| Snapshot metadata | 1 | 0.91ms | 4.33ms | 13.40ms | 81.02ms | 14.73x |
| Snapshot metadata | 5 | 1.77ms | 11.44ms | 13.73ms | 30.22ms | 7.76x |
| Snapshot metadata | 25 | 9.63ms | 21.19ms | 29.63ms | 52.82ms | 3.08x |
| Snapshot metadata | 100 | 46.19ms | 70.72ms | 138ms | 334ms | 2.99x |
Setup And Refresh Costs
Setup timings are not request latency, but they matter operationally because the proposed path needs data load and MV refresh before API reads.
| Scale | Rows | RDS load | RDS index/analyze | RDS total | StarRocks base load | MV refresh sum | StarRocks total |
|---|---|---|---|---|---|---|---|
| 100k | 100,000 | 1.6s | 2.5s | 4.2s | 2.6s | 13.5s | 29.5s |
| 1m | 1,000,000 | 13.8s | 16.4s | 30.4s | 5.9s | 26.8s | 46.4s |
| 10m | 10,000,000 | 144.6s | 190.9s | 335.7s | 33.3s | 63.9s | 110.8s |
Materialized View Refresh Detail
| Scale | MV | Refresh Time |
|---|---|---|
| 100k | filt | 1.8s |
| 100k | snap | 2.1s |
| 100k | content_published | 1.9s |
| 100k | content_rank | 1.7s |
| 100k | content_velocity | 2.0s |
| 100k | content_views | 2.2s |
| 100k | content_vpd | 1.7s |
| 1m | filt | 1.7s |
| 1m | snap | 1.8s |
| 1m | content_published | 4.0s |
| 1m | content_rank | 5.4s |
| 1m | content_velocity | 5.1s |
| 1m | content_views | 4.4s |
| 1m | content_vpd | 4.3s |
| 10m | filt | 1.8s |
| 10m | snap | 1.9s |
| 10m | content_published | 12.6s |
| 10m | content_rank | 14.6s |
| 10m | content_velocity | 11.2s |
| 10m | content_views | 11.3s |
| 10m | content_vpd | 10.6s |
Important StarRocks / S3 Tables Finding
Repeated appends into the same AWS S3 Tables Iceberg table failed from StarRocks with PreconditionFailed errors after the first append. This reproduced in a targeted StarRocks-only 2m test: the first 1m insert committed, the second 1m append failed after retries.
A single large insert did work. The successful 10m run loaded the content base table as one insert and used these StarRocks session settings:
- SET enable_adaptive_sink_dop = false
- SET pipeline_sink_dop = 1
- SET pipeline_dop = 1
- SET max_pipeline_dop = 1
- SET parallel_fragment_exec_instance_num = 1
Implication:
Interpretation
- For low-latency REST reads, RDS is still the stronger fit in this benchmark. It stays in the low single-digit millisecond range for the indexed 50-row endpoints at 10m rows.
- StarRocks MV latency is more stable than direct Iceberg querying, but it still has a baseline tens-of-milliseconds cost and degrades sharply on the category/country/language case at 10m rows.
- The denormalized model is appropriate for this API comparison. Because the tables are intended to be direct API equivalents, the payload parity checks are the right correctness gate.
- The next useful work is not another synthetic scale alone. The next report should add production-like filter cardinality, distribution skew, refresh frequency, cold/warm cache separation, and API service overhead from the actual application code path.
Benchmark Provenance
- Benchmark result JSON retained as local source data
- Native report page generated from the local benchmark notes
- Temporary AWS resources were removed after the run: S3 Tables benchmark tables, StarRocks secret, IAM inline policy, and runner temp files.