Skip to content

Releases: HarperFast/hnsw

Release list

v0.2.1

Choose a tag to compare

@kriszyp kriszyp released this 04 Sep 15:22
v0.2.1
42c7185

Fixes the local-minimum descent bug that blocked v0.2.0 (see 294d465): the upper-layer descent now beams at width 16 instead of greedy width 1, so a query can no longer be stranded in the wrong basin. v0.2.0 is superseded and should not be installed.

v0.2.0

Choose a tag to compare

@kriszyp kriszyp released this 03 Sep 00:02
95c8076

First release to ship prebuilt bindings. Installing @harperfast/hnsw now resolves a platform package (darwin-arm64, linux-x64-glibc, linux-arm64-glibc, win32-x64) and only falls back to a local Rust build when none matches (musl, or any other target).

Brings the crate up to the reviewed native delta of HarperFast/harper#2430 and adds a package-owned, durable way to invalidate a plane file the host cannot delete, so Harper can replace its vendored native/hnsw-plane with the released package.

Plane invalidation (new)

invalidatePlane(path) / invalidatePlaneAsync(path), and plane.invalidateFile() through a handle already held. A plane the host cannot unlink — a Windows sharing violation while another process maps it — must never be adopted later at its nonzero watermark, so invalidation is a one-way latch rather than a mutable tombstone: a flushAsync already in flight can still stamp the watermark word but cannot revive the plane.

  • Two markers, both attempted on every call, in band first: a sticky header byte under which getWatermark() reads 0 on every handle, plus a .stale sidecar created with create-new semantics and fsync'd along with its directory entry on POSIX. The call throws only when neither marker is durable, leaving the file exactly as found.
  • Enforced by the package: open() refuses a file carrying either marker (fail-closed — any directory entry at the sidecar name counts), and create() refuses a path with a leftover sidecar. An existing sidecar is re-synced through a no-follow, non-blocking open validated on the handle, so a planted symlink or FIFO is refused rather than followed.
  • invalidated() is the diagnostic that reports why a plane reads as empty.

Graph changes

  • First-insert claim/join, so concurrent first inserts cannot orphan each other.
  • Search-side entry repair with a rotating bounded probe, now with a futility latch: a fully dead graph stops probing after one empty rotation and re-arms on any node write through any handle, keyed on a header write epoch, instead of paying 1024 node reads per search forever.
  • A stale re-election can no longer CAS over an equal-level entry a fresh first-insert claim just installed.
  • Insert deletes the edgeless node it published when its bounded entry resolution falls out wedged.
  • Predicate-drain fixes.

Format

Format v7 — existing plane files are not readable and must be reindexed. The neighbor and upper-id arrays are 4-aligned (v6) and the invalidation latch byte is defined (v7). There is no read-only tolerance of v5/v6 files; 0.1.0 shipped without a consumer, so no migration path was built.

Full detail, including the reviewer-facing trade-offs, in #1.