What changed between the first release candidate and the shipped release. RC1 was a per-fix probe round + 3.5h soak (2026-07-21); GA was a targeted perf + fix re-verification (2026-07-23). Same test bed: 3× i4i.xlarge db (RF=3, 13 read-replica token ranges) + m5.xlarge app, Cassandra 5.0.8.
do_get counter deltas + EXPLAIN split counts (not per-query latency); GA measured 60-sample server-side elapsedTimeMillis. RC1's throughput came from a 3.5h soak; GA's from a 180s loadtest. Where a row isn't apples-to-apples it says so.| Fix | 0.16.0-rc1 | 0.16.0 GA | Change |
|---|---|---|---|
| #2806 / #2679 split pruning | REGRESSED / inert | FIXED | 13 splits (on==off) → 1 split; the connector now reads the bound key from the pushed-down filterJson on the table handle instead of the empty constraint.getSummary() |
| #2807 UDT keyspace-qual parse | BLOCKED (parse crash) | FIXED | rc1 code: Char crash at frozen<ks.udt> → all UDT tables now parse & are queryable |
#2349 scalar frozen<udt> | unreachable (behind #2807) | VERIFIED | decodes to {street: 2 Oak, zip: 10001}, matches sstabledump |
#2815 list<frozen<udt>> | column silently dropped (rc2) | symptom fixed | column now present in schema + SELECT *; elements are raw serialized UDT bytes (correct data, not yet structured — 0.17 candidate) |
The headline of the whole 0.16 cycle. In RC1 the optimization existed but never fired for the exact query class it targets:
WHERE key = ? | 0.16.0-rc1 | 0.16.0 GA |
|---|---|---|
| splits, pruning ON | 13 (inert) | 1 |
| splits, pruning OFF | 13 | 13 |
| ON vs OFF differential | none (identical) | 13 → 1 |
| per-query latency effect | not measurable (no fan-out change) | p50 −18% (185 → 152 ms) |
| coordinator CPU / query | — | −4× (8 → 2 ms) |
RC1's verdict was regressed → #2806: the pruning logic was correct in isolation but read the wrong source, so keyed reads always fanned out to all 13 read-replica ranges. GA reads the bound key where applyFilter actually put it, and the point read collapses to the single owning split.
| Round | Threads | qps | p50 | p99 | errors | Notes |
|---|---|---|---|---|---|---|
| soak-0.15 | 32 | ~39 | 798 ms | 1366 ms | 0 | 90-min steady block |
| 0.16.0-rc1 | 32 | ~32 | 990 ms | 1255 ms | 0 | 3.5h main soak (flat, 0 err) |
| 0.16.0 GA | 32 | ~33 | 967 ms | 1195 ms | 0 | 180s loadtest, pruning ON |
Throughput and latency are statistically unchanged rc1 → GA (~32 → ~33 qps, p99 ~1255 → ~1195 ms). The ~32–39 qps ceiling persists across both because it is owned by the fan-out skew (#2680) and snapshot-resolve floor, not by split pruning. What pruning does change under load is the cold ramp and tail: GA pruning-ON reaches steady state faster (15.7 vs 9.9 qps at 30 s with pruning off) and holds a tighter p99.
GA was a targeted perf + fix re-verify, so a few dimensions RC1 covered were not repeated. These are gaps in the comparison, not regressions:
| Dimension | 0.16.0-rc1 | 0.16.0 GA |
|---|---|---|
| Multi-hour drift soak | 3.5h, flat, 0 err | not re-run (180s loadtest only) |
| Integrity (start == end) | 1,703,038 == 1,703,038 | not re-run |
| Memory / fd / thread stability | RSS flat, 0 OOM/restart | not captured (no Grafana) |
| #2681 abort taxonomy | 0.026% genuine-error SLI | not re-measured |
| Snapshot grace-sweep | background 312→172, no query | not re-run |
The read-path stability characteristics were established sound in RC1's soak and there is no code path in the GA delta expected to change them; a full milestone soak on GA would confirm that if desired.