scene_service.persistence¶
Object persistence — survives scene restarts so the registry warm-starts instead of re-accumulating every stable object through the min_observations filter from scratch.
Backed by an embedded milvus-lite DB owned solely by the scene process (one .db file, single writer — no sharing with system/memory’s memsearch DB). Writes happen at the low-frequency scene-graph builder cadence; reads happen once at boot. Each row carries a caption embedding so a future G3 object-search layer can reuse the same table; when no embedder is wired (e.g. the VLM-fallback perception path that has no CLIP), a fixed placeholder vector is stored so the collection stays well-formed.
Rows are partitioned by the scalar map_id field: an object’s pose is only meaningful in the map frame it was observed in, so a restore must load rows written in that exact frame and nothing else. The web facade’s Save writes each snapshot under a fresh partition token (“<map_id>__s<seq>”, allocated by map_meta) rather than the bare map id — two saves of the same-named map belong to two different frames, and the token keeps them apart where a shared map_id partition would silently mix epochs. The primary key is the composite “{partition}::{object_id}” so the same object_id may exist in several partitions without colliding on upsert. All of this stays internal to scene and touches no atlas/MCP contract.
Classes
|
milvus-lite-backed store for SceneObject current-state. |
- class scene_service.persistence.ObjectStore(db_path: str, *, map_id: str = 'default', dim: int = 512)[source]¶
Bases:
objectmilvus-lite-backed store for SceneObject current-state.
Upsert is keyed by the composite “{map_id}::{object_id}” primary key, so a row holds the latest state of an object on a given map (no version history in v1 — version mirrors observation_count). All methods are synchronous; callers run them off the asyncio loop or inside an already-held registry lock as appropriate.
- close() None[source]¶
Close the client and release the milvus-lite server so the next boot can re-open the same .db file (mirrors memsearch’s cleanup).
- delete_map(map_id: str) int[source]¶
Delete persisted scene objects belonging to one map partition.
The collection is shared, so the filter is mandatory: deleting a map must never affect objects captured for another spatial map. The return value counts rows found by a query capped at 16384 — a “how much was there” signal, possibly lower than what the (uncapped) delete removed.
- load_all(*, partition: str | None = None, limit: int = 16384, strict: bool = False) list[SceneObject][source]¶
Return one partition’s persisted objects as SceneObject`s for warm restore (default: this store’s `map_id; rows of other partitions are filtered out).
Restored objects come back missing=True (known but not currently observed) with their persisted last_seen; re-observation flips them back via data association. The embedding column is intentionally not read back — restore needs only scalar state. A query error is logged at error level and yields an empty list rather than failing boot — UNLESS strict is set (the facade’s Load path), where the caller must be able to tell “snapshot unreadable” from “snapshot empty”: a Save on top of an unnoticed failed restore would commit an empty snapshot.
- property map_id: str¶
The sanitized map id this store is scoped to.
- persist(pairs: list[tuple[SceneObject, str | None]], *, partition: str | None = None) int[source]¶
Upsert (SceneObject, caption) pairs under partition (default: this store’s map_id; composite pk “{partition}::{object_id}”). Caption falls back to the class name when None. Returns the number of rows written; a milvus error is swallowed (logged) so a bad write never kills the builder tick — callers that need write integrity (the Save snapshot) compare the return value against len(pairs).
- purge_live_partitions() int[source]¶
Best-effort deletion of leftover .live* partitions.
Earlier builds persisted the live session under a unique .live-<pid>-<ts> partition every boot and never cleaned it, so a long-lived DB accumulates dead rows without bound. Live sessions are no longer persisted at all; this reclaims what old boots left behind. Returns the number of rows deleted (0 on any error — cleanup must never block a boot).