Skip to content

v1.4.0 — durable acks, bounded non-numeric query plans, coalesced reads

Choose a tag to compare

@funkBuild funkBuild released this 22 Jul 07:08
· 47 commits to main since this release

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 aggregationInterval no 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 every wal_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_INCOMPLETE responses, 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