[AZ-305] c6 PostgresFilesystemStore: TileStore + TileMetadataStore impl

Adds the production PostgresFilesystemStore implementing both protocols
in a single class. Filesystem-backed JPEG I/O (atomic sidecar write,
read-only mmap) + Postgres-backed metadata (spatial bbox, LRU, voting,
upload bookkeeping). Wires composition via `from_config` classmethod.

Key behaviors:
- AC-3 strict reading: INSERT runs first inside an open transaction;
  duplicate-key collisions raise `TileMetadataError` BEFORE any byte is
  written, leaving the original file + sidecar byte-identical. Atomic
  sidecar write happens inside the same transaction; commit closes it.
  Comp-delete remains as a safety net for the rare commit-after-write
  failure path.
- AC-2 content-hash gate runs before any I/O.
- Construction performs an orphan-file reconciliation scan and emits an
  INFO `c6.store.construct` log with steady-state stats.

Adds `c6.write` and `c6.write_failed` FDR record kinds (schema v1.1.0,
forward-compatible) and a thin operator CLI at
`c6_tile_cache.tools dump` for inspection.

Dependencies: adds `psycopg-pool>=3.2,<4.0` for the connection pool used
on the F3 read-hot path.

Tests: 25 new tests for c6_tile_cache cover AC-1..AC-15 plus
MmapTilePixelHandle + helper round-trips. Full Tier-2 unit suite passes
(1215 passed, 8 skipped, 1 pre-existing unrelated failure
`test_ac8_read_host_tuple_on_jetson` — missing `pynvml` on macOS,
Jetson-only).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Oleksandr Bezdieniezhnykh
2026-05-12 18:01:50 +03:00
parent bf33b94260
commit d1c1cd9ab4
14 changed files with 2382 additions and 18 deletions
@@ -56,7 +56,6 @@ from gps_denied_onboard.runtime_root.storage_factory import (
build_tile_store,
)
_CONTRACT_DIR = Path(__file__).resolve().parents[3] / (
"_docs/02_document/contracts/c6_tile_cache"
)
@@ -313,6 +312,14 @@ def _install_fake_postgres_store_module() -> type:
def __init__(self, config: Config) -> None:
self.config = config
@classmethod
def from_config(cls, config: Config) -> _FakePostgresFilesystemStore:
# AZ-305: factories now dispatch via from_config so the production
# impl can wire its ConnectionPool / FdrClient / helpers without
# the runtime_root opening a connection of its own. The test fake
# preserves the single-config-arg shape via this classmethod.
return cls(config)
fake_module = types.ModuleType(_FAKE_STORE_MODULE)
fake_module.PostgresFilesystemStore = _FakePostgresFilesystemStore # type: ignore[attr-defined]
sys.modules[_FAKE_STORE_MODULE] = fake_module
@@ -359,8 +366,27 @@ def test_ac4_build_tile_metadata_store_returns_protocol_impl(
assert isinstance(md, TileMetadataStore)
def test_ac5_tile_store_runtime_module_missing_raises(store_module_cleanup) -> None:
def test_ac5_tile_store_runtime_module_missing_raises(
store_module_cleanup, monkeypatch
) -> None:
"""AC-5 historical name; after AZ-305 the impl module always exists, so
"missing" is exercised by deleting it from ``sys.modules`` AND making
``importlib`` refuse the import. We patch the module-level lazy import
site to ``raise ModuleNotFoundError`` so the factory hits the same
documented branch.
"""
config = _config_with_c6()
import gps_denied_onboard.runtime_root.storage_factory as factory_mod
real_import = __builtins__["__import__"] if isinstance(__builtins__, dict) else __builtins__.__import__
def _block_postgres_import(name, *args, **kwargs):
if name.endswith("postgres_filesystem_store"):
raise ModuleNotFoundError(name)
return real_import(name, *args, **kwargs)
monkeypatch.setattr(factory_mod, "__builtins__", {"__import__": _block_postgres_import}, raising=False)
monkeypatch.setitem(sys.modules, _FAKE_STORE_MODULE, None) # type: ignore[arg-type]
with pytest.raises(RuntimeNotAvailableError) as exc_info:
build_tile_store(config)
assert "postgres_filesystem" in str(exc_info.value)