Backend storage

httk.store.backend.sql stores plain frozen dataclasses in a relational database (SQLite, DuckDB, or PostgreSQL), makes them queryable through a backend-agnostic search DSL, and serves them through the neutral httk.core.EntryProvider contract — no SQLAlchemy types in the public API, no base class to inherit:

from httk.store.backend.sql import Backend, SqlStore

db = Backend.sqlite("results.sqlite")
store = SqlStore(db, entry_records={})   # first open declares the store
# reopen later with just: SqlStore(db)

with store.transaction():
    sid = store.save(record)             # dedups and recurses automatically

same_record = store.fetch(type(record), sid)   # a lazy row; add eager=True to materialize

Records are content-addressed (content_id) as well as locally numbered (sid), and identical content saves to one row however many times it arrives.

Store timestamps are enabled by default. They support historic predicates such as store_timestamp <= T; configure their unit size with store_timestamp_resolution (default: microseconds, time_ns() // 1000). The detailed guide covers the query syntax, deduplication semantics, clock guard, and fsck repair behavior.

Append-only record replacement is available too: store.replace(predecessor, obj) saves a logical successor sharing the predecessor’s lineage, store.history() walks a lineage, and store.searcher(only_latest=True) restricts root variables to each lineage’s latest row.

An entry may also carry named alternative representations — a conventional cell beside a primitive one, say: store.save(obj, alternative_of=<main entry id>, alternative_kind="conventional") stores a sibling that shares the main’s public id (addressed as <id>~<kind>, with its own revision lineage), while ordinary queries stay mains-only by default (only_main_alt=True). See the detailed guide.

The full guide, Backend storage in detail, covers declaring storable classes with the httk-core marker vocabulary, entry families and multi-record dispatch, the search DSL and stored properties, record replacement lineages, bulk ingestion (including bulk_ingest(workers=N) and the crash-safe finalize="deferred" fresh-store profile), the permanentization role model with store.fsck() recovery, OPTIMADE serving, and store-layout versioning.