Releases: Hikari-Systems/slater
Release list
v0.24.1
v0.24.1 — correctness fixes, cache scalability, and an internal refactor
Two correctness fixes, a read-path scalability improvement, and a structural
split of the two largest source files. No storage-format change; no config change.
Correctness
- toInteger() on an out-of-range or non-finite float now returns a clear error
instead of silently saturating (1e19 became i64::MAX, NaN became 0).
toIntegerOrNull() returns NULL for those same inputs, and toInteger("abc")
stays NULL as before. The same guard closes the integer index-argument path
(list.remove / list.insert / …), which had the identical trap. - The auto-consolidation log classifier branches on a typed error code rather
than matching message text, so a benign "already in progress" single-flight
race can never be mis-logged if the wording changes.
Performance
- The query-result cache and the vector-index cache (resident PQ codes + the
Vamana block LRU) now use a CLOCK second-chance eviction with a shared-read-lock
hit path, instead of a global mutex that rewrote an ordered LRU on every hit.
Concurrent cache hits no longer serialise: measured roughly 2x aggregate hit
throughput across four threads, where the previous path fell below parity as
cores were added. This brings both pools in line with the decompressed-block
cache, which already had this design.
Internal (no behaviour change)
- The Cypher executor and the Bolt server — the two largest files in the tree —
are each carved into cohesive submodules: the executor Engine into
exec/{access, driver, matchclause, proc, knn, traverse, scan, project, eval},
and the server into server/{registry, conn, listen, handle, write,
consolidate, query}. Pure relocations; the full test suite is unchanged.
Full Changelog: v0.24.0...v0.24.1
v0.24.0
slater 0.24.0 — dot-product (MIPS) vector search + a security & correctness hardening pass
Makes dot-product (inner-product / MIPS) a first-class vector metric and folds in a
whole-codebase review remediation. Builds on the v0.23.0 FreshDiskANN writable vector
ladder. No on-disk format-version change — existing generations open unchanged.
Dot-product (MIPS) vector search — now properly supported (HIK-137)
Dot-product kNN previously recalled only ~0.32–0.50: the graph reached inner-product
neighbours via a norm-augmentation reduction that navigates poorly under wide norm
spreads. 0.24.0 adds an IP-native navigator — inner-product closeness, top-R-by-IP
neighbour selection, highest-norm entry, and an IP-trained PQ codebook — across the whole
write ladder: delta, sealed segment, consolidate, merge and delete. On the measurement
fixture recall@10 rises from ~0.40 to ~0.99–1.0 and holds rung-to-rung, verified against an
independent brute-force inner-product ground truth. The navigator is an additive, optional
discriminator: cosine and L2 navigation is unchanged, and a forged inner-product navigation
on a cosine/L2 index is refused rather than mis-served.
Security & correctness hardening (whole-codebase review)
- Session state no longer outlives the identity it belonged to — LOGOFF / failed re-LOGON
could leave one connection's buffered rows and open-transaction graph readable by the next
user on that connection, bypassing the read ACL. (HIK-123) - A label removal is a scope change, not an embedding delete — consolidation no longer
permanently destroys a de-labelled node's vector. (HIK-122) - Disk cache is bounded across restarts (adopts prior-run files, LRU-evictable) and its
write-behind queue is byte-bounded — closing an unbounded-disk path and an unaccounted-RSS
path. (HIK-124, HIK-125) - On-disk widths, offset tables and PQ code bytes are validated where they enter memory — a
corrupt or forged block now errors instead of panicking or silently returning wrong
results. (HIK-126, HIK-128, HIK-132, HIK-133) - Non-finite embedding components are rejected at ingest and query; out-of-range duration
components are rejected instead of saturating to a wrong value; WAL tail and commit-marker
handling corrected. (HIK-134, HIK-127, HIK-130, HIK-135) - User range indexes are retained on the pk property during build. (HIK-129)
Tooling & tests
- A committed, reproducible performance suite for the vector write ladder, with recall scored
against an independent brute-force ground truth (docs/PERF-REPORT.md). (HIK-120, HIK-121) - CI now compiles and runs the s3/gcs feature-gated test code that the default build never
touched — the blind spot where the disk-cache bugs had survived. (HIK-138)
Upgrade notes
No format-version change; v0.23.0 generations open as-is. Dot-product graphs opt into the new
IP-native navigation; cosine/L2 behaviour is unchanged.
Full Changelog: v0.23.0...v0.24.0
v0.23.0
slater 0.23.0 — FreshDiskANN: writable disk-native vector search
This release makes embeddings a first-class writable value. Vector search was
previously a read-only, build-time index (cosine only); it is now a full write ladder
you can insert into, update, and delete from in place — without an offline rebuild —
and it gains the L2 and dot-product (MIPS) metrics alongside cosine.
Highlights
- Writable embeddings.
SET n.embedding = vecf32([…])andREMOVE n.embedding
land in the write delta and are immediately KNN-visible with exact rank, then survive
a segment flush, a merge, and a consolidation. - A three-level read path. A query merges a sealed base index, a sealed per-segment
index, and an in-memory RW-index (a live mutable Vamana over the delta), so KNN
latency stays flat as writes accumulate instead of growing with the pending-write
count. - Deletes stop costing query IO. A deleted vector becomes a navigational hole that a
background delete-consolidation splices out of the graph; measured ~3x fewer node
fetches per query at 67% deleted, at equal recall. - Consolidation without a rebuild. Because the on-disk graph addresses neighbours by
layout position rather than node id, a consolidation carries the Vamana graph by
reference — hard-linked, byte-identical — and rewrites only a small id column, folding
vector writes into the base without the O(N) graph reconstruction. - cosine / L2 / dot indexes (dot via a norm-augmentation transform).
- A committed performance bench suite (
crates/slater/benches/) and a written report
(docs/PERF-REPORT.md).
Upgrade note
- On-disk format 7 → 8: rebuild required. Existing generations must be rebuilt with
the 0.23.0 builder; the server refuses an older format legibly rather than misreading
it. There are no legacy installs to migrate.
Full Changelog: v0.22.1...v0.23.0
v0.22.1
slater v0.22.1
Whole-codebase review remediation: 46 findings fixed — 40 from a full
security/correctness/performance review of the codebase, plus 6 follow-ups
discovered while fixing those — each landed with a regression test and a
clean-room verification of the integration branch.
Security & robustness
- Every untrusted-input decode path is now bounded: the Bolt PackStream decoder,
zstd block decompression, the on-disk column/segment/index decoders, the
property-value wire format, and query nesting depth. A malformed or hostile
input can no longer drive an unbounded allocation or a stack-overflow abort. - The whole pre-authentication connection window is bounded — TLS handshake,
reads, and writes each on one deadline — and password verification and write
execution now run off the async reactor behind concurrency caps, so a
connection or login flood can no longer stall the server for everyone. - Object-store integrity re-hashes an object body when the store carries no
server-side checksum, instead of silently falling back to a length-only check.
Correctness
- ORDER BY direction and DISTINCT are read from the parsed keyword rather than a
substring scan of the clause text. - Graph export round-trips every node label and backtick-quotes identifiers, so a
dump rebuilds faithfully and cannot inject into the rebuild. - The write-ahead log rolls back a partially written batch and recovers from a
poisoned lock, closing the case where an unacknowledged write could be
resurrected on replay; segment creation fsyncs its parent directory. - Integer arithmetic overflow now errors cleanly instead of wrapping silently,
rand() is uniform over [0,1), and relationship-uniqueness is enforced across
fixed-length patterns.
Performance
- The block cache and the decoded-block cache are sharded with a lock-free read
path, so hit throughput scales with cores instead of collapsing under
contention. - Anchor scans stream in bounded windows, so LIMIT short-circuits and resident
memory stays bounded whether or not a write delta is present.
Resident memory remains bounded independent of graph size: a 90M-node /
1.5B-edge graph serves within the documented few-hundred-MB target.
Full Changelog: v0.22.0...v0.22.1
v0.22.0
slater v0.22.0
Live, durable writes — and a re-planed, smaller on-disk format. Slater still
serves graphs bigger than RAM over Bolt at a fixed cache budget; this release adds
a durable write path on the same immutable image and generalises the storage codecs
so the format is smaller and usable in its encoded form.
Writes (the headline)
- A durable write layer over the immutable core: writes land in an in-RAM memtable,
flush to immutable L0 delta segments, and fold back into the core by background
consolidation — all over standard Bolt, so existing drivers write as well as read.
Group-committed batches (write-UNWIND), off-heap L0, size-tiered and fraction-of-core
auto-consolidation with a hard-cap throttle, and an off-peak consolidation window. - Business-key MERGE / SET / DELETE on the live graph: create-on-absent, patch a core
node/edge in place (including relocating a moved indexed value), properties on
delta-born nodes and edges, delete by business key. The read cost of a write scales
with the delta, not the graph: count(*) and label/reltype marginals stay metadata
reads with writes outstanding. - GQL INSERT write spelling alongside Cypher.
Storage format (FORMAT_VERSION 7 — no back-compat, rebuild generations)
- A generic per-chunk "plane" codec (Elias-Fano / RLE / bit-packed / raw / zstd,
chosen per chunk) backs the degree column, resident key columns, endpoint postings,
node labels and the topology CSR — usable encoded, no decompress on the hot path. - Topology carries a per-reltype directory: a typed expansion decodes only the matching
relationship type's runs. - Codec discipline: every codec carries its Constant/RLE/complement co-candidate rather
than committing to one — a dense endpoint set stores its sparse complement, a
single-label column is a run, and a constant chunk is just a single-run RLE. On the
91.6M-node / 1.49B-edge reference graph this keeps node_labels at 242 MB (not 1.1 GB)
and the reltype postings under a megabyte, all decompress-free.
Query performance
- k-hop count(endpoint) summed over a maintained per-node degree column instead of
walking the final hop; hub-degree streaming bounds multi-hop counts on dense graphs.
Full Changelog: v0.21.0...v0.22.0
v0.21.0
slater v0.21.0
External-build memory fixes for the large-graph pipeline. Both cut peak RSS
sharply on the 91.6M-node / 1.49B-edge reference build with a byte-identical
generation (content-hash unchanged), verified end-to-end.
Highlights
- resolve: the edge-endpoint resolve phase no longer overshoots its memory
budget. Its per-partition external sorters now spill inline instead of through
the shared parallel spill pool, which had multiplied each sort's run count (and
therefore the k-way merge's resident block footprint) inside an already
thread-saturated stage. Peak RSS 9.33 GB -> 2.61 GB; phase ~11% faster. - cluster: the locality-reorder adjacency sort gets its full memory budget back.
A single non-concurrent sort had been starved 16x under a convention meant for
phases that fan out many concurrent sorters, which inflated run count and hurt
both memory and speed. Peak RSS 6.54 GB -> 1.70 GB; phase back below its
pre-regression wall time.
Both fixes are output-preserving: every sort key involved is a total order, so
run count cannot change the merged result.
Full Changelog: v0.20.0...v0.21.0
v0.20.0
v0.20.0
Query engine:
- Whole-graph label/reltype metadata (label/reltype counts, self-loop counts,
first-label counts, the (srcLabel, reltype, tgtLabel) schema cube, undirected
and labelled variants) is now answered directly from resident manifest
metadata for unanchored MATCH shapes — zero block reads, versus a full
unanchored scan before. Now also composes with ORDER BY / LIMIT. - Fixed a correctness bug where MATCH (a)-[r]->(a) (same variable on both
ends, i.e. a self-loop constraint) was incorrectly treated as two bare
endpoints by the metadata fast path; it now declines to the matcher.
Build (slater-build):
- Fixed a real bug where the offline builder's emit.graph_summaries phase
(whole-graph label/reltype/schema-cube summaries, computed at build time)
re-decompressed a node's entire on-disk block on every single per-node
lookup, with zero caching. At real dataset scale this made the phase
effectively unbounded (confirmed via profiling: never finished after 27+
minutes, on pace for 20-30+ hours). Fixed via a small, block-sequential
cache shared with the query engine's existing block-cache design
(extracted into a common module so both crates use the same well-tested
cache) — the phase now completes in well under 5 minutes even on a
91M-node / 1.5B-edge generation. - Added an opt-in
--features profilingbuild (off by default, verified
absent from the default dependency graph) for heap-profiling the builder
during future investigations.
Known issues carried into this release (not yet fixed, flagged intentionally
rather than silently shipped):
- The external
resolvepass (business-key edge resolution) has a real
memory-accounting gap: observed resident memory during this phase runs to
several GB against a nominal much smaller configured working-memory budget,
on large generations. Root-caused in part (a fixed-size internal buffer
pool whose aggregate footprint isn't counted against the configured
budget), but a further, size-scaling contributor is not yet isolated. - An attempted fix to the offline builder's clustering pass (bounding its
internal sort's memory the same way other passes do) regressed both wall
time and peak memory at large scale instead of improving them, and needs
to be revisited or reverted.
Neither affects correctness of the resulting on-disk generation — both are
performance/resource-usage issues in the build path only.
Full Changelog: v0.19.0...v0.20.0
v0.19.0
v0.19.0 — CASE and operators in the build-time SET dialect
slater-build can now ingest business-key MERGE dumps whose SET right-hand
sides use CASE expressions and infix operators, not just function calls,
literals, and same-node property references.
Highlights
- Build-time
SETgains a full scalar-expression grammar:CASE
(searched and simple), comparison (=,<>,<,<=,>,>=),
arithmetic and string concatenation (+,-,*,/,%), and
booleanAND/OR/NOT, with standard precedence and parentheses. - A common accumulator idiom now builds unmodified, e.g.
SET n.summary = CASE WHEN coalesce(n.summary,'') = '' THEN x ELSE n.summary + '; ' + x END. Each assignment folds against the node's
accumulated properties in input order, so the result matches a live graph
engine's — without the quadratic read-grow-rewrite cost. - Operator, comparison, and truthiness semantics are shared with the query
engine throughslater-scalar, so build-time and query-time evaluation
agree (string-concat+, three-valued=/NULL handling, Kleene booleans). - CASE branches evaluate lazily: only the selected
THEN/ELSEruns.
Scope and compatibility
- Edge
SETand overlay-patchSETremain literal-only. - Existing dumps are unaffected; this is strictly a superset of the prior
dialect.
No configuration changes are required.
Full Changelog: v0.18.0...v0.19.0
v0.18.0
slater v0.18.0
Faster cold start, and per-query timing at the info level.
Startup now opens every graph — and, within each, its range indexes and
per-file integrity checks — concurrently rather than one object at a time.
On an object-store backend a fresh process previously streamed hundreds of
small files serially (a HEAD per inventory file, a footer read per range
index) before the Bolt listener bound, so a large multi-graph deployment
could take the better part of a minute to accept connections. The opens
are independent, so fanning them out cuts cold start to roughly the slowest
single graph. Validation, the readers, and the on-disk format are
unchanged, and the first error still aborts the open.
The per-query query executed metrics summary now logs at info instead of
debug, and the default log level is now info. The summary therefore appears
out of the box without the chatty debug SDK and wire tracing; the
instrumentation that produces it is gated at info as well, so raising the
level to warn restores the zero-overhead hot path.
No format, on-disk, or wire changes; upgrade in place.
Full Changelog: v0.17.1...v0.18.0
v0.17.1
slater v0.17.1
Per-query observability on every query, not just the CLI.
The query executed metrics summary that the query CLI subcommand
emits is now produced on the Bolt path as well, and both carry the same
figures:
- query cost (elements charged against the budget)
- execution / encode / total timings and the result row count
- result-cache hit or miss
- block-cache hit/miss delta for the query, plus the hit ratio
The summary is gated on the debug log level — when it is off the hot
path takes no timestamps and no cache snapshots — and the default log
level is now debug, so the summary appears out of the box. Raise the
level to info to silence it and restore the zero-overhead path.
The startup line now reports the compiled build version.
No format, on-disk, or wire changes; upgrade in place.
Full Changelog: v0.17.0...v0.17.1