v1.4.0 — durable acks, bounded non-numeric query plans, coalesced reads
Highlights
This release addresses the multi-second bucketed queries and reactor stalls reported on bool-heavy IoT measurements, and closes a write-durability gap.
Query performance — many-series and boolean/string workloads
- Boolean series with an
aggregationIntervalno longer materialise their full raw range. They ride the bucketed-LATEST pushdown (sparse-stat resolution / early-terminating reverse scan for coarse grids, the batched general pushdown for dense grids), answering from the few blocks that decide buckets. Incident shape (8 bool series × 3M points): whole-range bucket 1717ms → 0.5ms, 1h buckets 1677ms → 49ms, 5m buckets 1690ms → 54ms. Results are bit-identical to the canonical LATEST-per-bucket semantics — pinned by a new plan-equivalence test across memory/TSM/overlapping-file placements. - String series (and LWW-gated booleans) use the bounded chunked reader as their normal path: peak memory of one chunk instead of the whole range, with reactor yields throughout. The non-numeric reduction loops are fully yield-chunked — the 80–150ms reactor stalls seen in production are gone.
- Cross-series DMA read elevator: concurrent series' block reads against the same TSM file are sorted, gap-merged (≤128KB) and issued as a few large reads instead of one small random read per (series, file). Measured on 960-series queries: 4,951 → 1,161 ops (raw, multi-file), 998 → 84 ops (bucketed LATEST). On IOPS-throttled volumes this translates directly to latency.
- Dense bucket grids (>256 buckets) for LATEST/FIRST now use the batched read plan instead of per-block reverse scans (same answers, ~100× fewer read ops on fine grids).
Durability — acknowledged writes survive crashes
- New
[storage] wal_sync_mode, default"always"(group commit): a write is acknowledged only after its bytes are flushed and fdatasync'd. Concurrent writers share one flush round, so the cost amortises: insert throughput is unchanged (11.9M pts/s warm on the reference bench) and p99 batch latency improved (38.5ms → 21.9ms). Previously, a SIGKILL/OOM-kill silently lost the buffered tail of acked writes — up to 256KiB per shard-WAL with no time bound, hitting compressed boolean series 10–100× harder than float. The kill-test that lost 60k/12k/1k acked points now loses zero. "interval"mode (ack-immediate, periodic flush everywal_sync_interval_ms) is available for fsync-hostile storage;"rollover"preserves the legacy behaviour.- WAL segment preallocation now follows
wal_size_threshold(was a stale 16 MiB).
Diagnostics
- TSM read/decode failures now name the file:
... CompressedSlice - attempted to read beyond buffer bounds [tsm shard_1/tsm/000123.tsm]— in the dropped-series log,QUERY_INCOMPLETEresponses, and compaction errors. Corrupt blocks can finally be traced to the file that holds them.
Upgrade notes
- No format changes; TSM/WAL files from v1.3.0 read unchanged.
- The durability default changed from the legacy volatile-until-rotation behaviour to group commit. On fsync-slow volumes, if ingest latency regresses, set
wal_sync_mode = "interval".
All suites green: 3,591 C++ unit / 46 perf / 184 jest / 8 standalone.
🤖 Generated with Claude Code