releaseshybriddb3 min read

HybridDB 0.8.0 — one identity across the store

Chroma vectors are now keyed by the logical primary key, killing a whole bug class at the root: the TEXT-PK journal wedge and the repeated rowid→pk mapping bugs.

Release notes, HybridDB 0.8.0 (2026-09-03). This is also the first post in the “what shipped” series — every release gets one of these, tied to the changelog.

The change

Chroma vectors are now keyed by the logical primary key (str(pk)), not the physical rowid — one identity shared across SQLite, the DuckDB mirror, and Chroma:

  • Per-collection identity marker (hybriddb:identity: pk|rowid) in Chroma collection metadata.
  • New collections are pk-keyed from creation; pre-0.8 collections keep rowid keys until migrated — upgrading changes nothing until you opt in.
  • migrate_vector_identity(table=None) re-keys legacy collections by copying embeddings (never recomputed — asserted bit-identical) and deleting orphan rowid-keyed vectors; idempotent, covers all longtext collections, default rowid-alias tables are no-ops.

Why it matters

This deletes at the root a bug class we’d been chasing across three releases:

  • The TEXT-PK journal wedge — DuplicateIDError on delete+reinsert (TEXT-PK tables reuse rowids, so delete-then-reinsert produced duplicate Chroma ids and permanently wedged the journal)
  • The repeated rowid→pk mapping bugs (0.5.4/0.5.5, and the CoreMem variants that inherited them)
  • Read paths unified on pks: FTS keyword ids, vector ids, and row fetches all use the pk; reconcile is scheme-aware

There’s a regression test for the delete+reinsert scenario, and the migration is copy-only — embeddings are never recomputed, so re-keying can’t silently change your semantics.

What came with it

0.7.0 (last month) shipped metadata pre-filtering — where= pushes scalar-column filters into the Chroma scan before the vector query — and made DuckDB mirrors lazy (tables you never query with olap cost nothing). Operator-form filters are now enforced in keyword mode too, and versioned-table rollback got the batched-path treatment (~20× removal-heavy, ~37× update-heavy at 100k rows). Full detail: the changelog.

Using it

pip install hybriddb -U
db = HybridDB("./data")                    # new collections: pk-keyed automatically
db.migrate_vector_identity()               # legacy collections: opt in, idempotent
db.verify_chain("memories")                # versioned tables unchanged

Docs: API reference · Concepts. Benchmarks and methodology: the stack post.