Compatibility, Migration, and Recovery
Compatibility Contract
GestaltDB 0.x has no 1.0 semantic-versioning guarantee, but persisted-data changes are additive: current graph records remain readable, temporal history uses a versioned and checksummed envelope, and sampler loaders either validate a known format or fail closed. Documented public classes and methods are the supported application surface. Underscore-prefixed methods, storage key layouts, benchmark helpers, and serialized Python objects are implementation details.
The rule, truth-maintenance, modal, bitemporal Cypher, and temporal snapshot APIs are public but evolving. Pin the GestaltDB minor version when deploying them and exercise a restored copy of the database before upgrading. Arbitrary in-place format conversion and opening one database with a different serializer are not supported.
Backend and Serializer Matrix
The temporal version, claim, rule, modal, and read-view APIs use the same contract on every available backend and with Pickle, JSON, MessagePack, and Protobuf serializers. Claims, rule definitions, commit descriptors, and modal accessibility metadata use canonical serializer-neutral JSON; node and edge payloads use the database serializer.
Backend |
Temporal publication |
Optional package |
|---|---|---|
LMDB |
Native transaction across records, indexes, descriptor, and marker |
|
LevelDB |
Marker-last publication; incomplete commits remain invisible |
|
PyRex/RocksDB default |
Marker-last publication; one externally serialized temporal writer |
|
PyRex/RocksDB transactional |
Native graph transaction; no native columnar writer |
|
Serializer |
Temporal node/edge payloads |
Legacy |
|---|---|---|
Pickle |
Supported |
Supported for historical datetime payloads |
JSON |
Supported for JSON-compatible properties |
Not supported for built-in datetime legacy payloads |
MessagePack |
Supported when |
Not supported for built-in datetime legacy payloads |
Protobuf |
Supported when |
Not supported for built-in datetime legacy payloads |
Missing optional packages raise an actionable ImportError when the feature
is used. They are not required to import the base package. The test suite skips
only unavailable backend/serializer matrix entries.
Legacy Edge Migration
TimeIndexedEdge is a deprecated current-state key convention, not temporal
history. Back up the complete database, stop concurrent writers, open it with
the serializer that originally wrote it, and run:
migrated = graph.migrate_time_indexed_edges(
delete_legacy=False,
metadata={"operator": "release-2026-09"},
)
graph.close()
Reopen the database, validate representative get_edge_as_of and temporal
Cypher reads, then call migrate_time_indexed_edges(delete_legacy=True) to
remove legacy records. The migration groups records by logical edge ID. A record
is valid from its timestamp to the next timestamp for that ID; the final record
is open-ended. It appends one marker-backed temporal commit and does not
materialize mutable Edge records.
Retries skip exactly matching migration assertions. Cleanup begins only after a
visible temporal commit, so an interrupted cleanup can be rerun. Duplicate
timestamps, malformed payloads, mismatched timestamp keys, or legacy records
changed after a successful run fail before another commit is published. Do not
add new TimeIndexedEdge records after migration; use put_edge_version.
Snapshots are immutable exports, not databases. Format-v1 snapshots remain loadable without temporal or integrity guarantees. Rebuild from the source database to obtain authenticated format-v2 snapshots; there is no in-place v1 upgrade. Unknown versions, incomplete publication, checksum mismatches, invalid array shapes, or provenance mismatches fail closed.
Recovery and Retention
For deferred writes, indexed reads fail until
rebuild_temporal_indexes() or rebuild_deferred_indexes() succeeds.
Derived indexes may be rebuilt from canonical history. A marker-last crash can
leave reserved records, but an unmarked commit is invisible and the next commit
uses a new ID. Filesystem-backed stores keep the durable allocation high-water
mark in .gestaltdb_temporal_sequence and serialize writers with
.gestaltdb_temporal_writer.lock. Include both files in whole-database backups
and never delete or edit internal metadata or sidecar files manually.
Inspect failed publications before deleting anything:
report = graph.list_temporal_orphans()
for artifact in report.artifacts:
print(artifact.commit_id, artifact.kind, artifact.present_record_count)
After confirming an inclusive recovery horizon, reclaim only through that ID:
result = graph.reclaim_temporal_orphans(through_commit=report.through_commit)
Reclamation never lowers the allocation high-water mark or reuses commit IDs. It validates unpublished descriptors and records, deletes their derivable index entries before canonical artifacts, and rebuilds indexes from visible history. Malformed artifacts fail closed. Cleanup is transactional where the backend supports graph transactions; LevelDB and default PyRex cleanup is ordered and retryable, and physical disk space may require backend compaction.
Temporal index rebuilds populate an isolated generation, authenticate it against
the visible commit-marker digest, atomically activate it, and then delete entries
from the previous generation. An interrupted build leaves the prior generation
active. A visibility checksum mismatch raises TemporalCorruptionError;
rebuild only after canonical history has been validated or restored.
Hash, envelope, descriptor, marker, or canonical-record corruption raises
TemporalCorruptionError. Index rebuilds do not repair corrupted canonical
history: stop writes and restore the complete backend directory from a known
good backup. Copy databases only while closed or with a backend-supported
consistent backup mechanism. Snapshot corruption is recovered by rebuilding the
snapshot from a verified read view.
Visible temporal history, claims, rules, and commits have no pruning or TTL API. Orphan reclamation removes only unpublished artifacts. Retain all canonical history and include it in capacity plans. Deleting mutable graph records does not delete temporal versions, and deleting old snapshots does not affect source history.