Headline
`memory_create` / `memory_update` drop from 10s+ to ~2s per call on the D1 backend — 5× faster.
Three independent fixes, all landing together:
- Cached `ensure_schema()` per backend instance — previously ran 7–9 D1 round-trips on every MCP tool call (~4–8s wasted each). Now runs once per backend per process. [`memora/schema.py`]
- Single paginated LEFT JOIN for crossref/vector scan — replaces the old `list_memories(...) + _get_embeddings_for_ids(...)` two-step that cost ~10 sequential D1 round-trips on stores with many memories. [`memora/storage.py`]
- Per-instance D1 session token + backend-level keep-max bookmark mirror — the old class-level token could be stomped by cross-thread code (`cloud_sync`'s `threading.Timer`), corrupting read-your-writes. Now safe under concurrency. [`memora/backends.py`]
Also in this release
- Durable Object request reduction — WebSocket keepalive ping removed, broadcasts skipped when no clients connected, disconnect on hidden tab. Direct Cloudflare cost savings.
- Schema cache correctness — `CloudSQLiteBackend.sync_before_use()` can `shutil.move` a fresh DB file over `cache_path`; the cache now invalidates via inode-based signature so long-lived processes don't run against a post-replacement DB with stale schema assumptions.
- Security (merged from main) — XSS attribute injection fix in graph UI (#36), stale draft cancellation (#37).
- Docs — eventual-consistency model of the `related` graph documented in `memory_related` and `memory_rebuild_crossrefs` MCP tool docstrings.
Upgrade notes
- No schema migration required.
- No public API signature changes.
- No configuration changes required.
- New optional env var `MEMORA_VECTOR_SCAN_PAGE_SIZE` (default: 1000) — rarely needs tuning.
- 39/39 tests passing.
Verified performance
Measured against live D1 (steady-state, medium-length memory):
| Operation | Before | After |
|---|---|---|
| `storage.connect()` 1st | — | ~1.9s |
| `storage.connect()` 2nd+ | ~4–8s (schema probe) | ~0ms (cache hit) |
| `add_memory` | 10s+ | ~1.8s |
| `update_memory` | 10s+ | ~1.1s |