Coverage for node / src / stigmem_node / storage / base.py: 77%

22 statements  

« prev     ^ index     » next       coverage.py v7.13.5, created at 2026-05-25 01:49 +0000

1"""StorageBackend abstract base — the seam between node logic and persistence.""" 

2 

3from __future__ import annotations 

4 

5from abc import ABC, abstractmethod 

6from collections.abc import Generator 

7from contextlib import contextmanager 

8from pathlib import Path 

9from typing import Any 

10 

11 

12class StorageBackend(ABC): 

13 """Pluggable storage seam. 

14 

15 Implement this class to add a new persistence backend. The interface covers: 

16 - Connection lifecycle (``connection``) 

17 - Transaction semantics (commit on clean exit, rollback on exception) 

18 - Migration runner (``apply_migrations``) 

19 - Snapshot hooks (``export_snapshot`` / ``import_snapshot``) 

20 

21 Fact CRUD and query primitives are expressed as raw SQL through the 

22 SQLite-API-compatible connection object yielded by ``connection()``. 

23 """ 

24 

25 @property 

26 @abstractmethod 

27 def backend_name(self) -> str: 

28 """Short identifier returned in ``/.well-known/stigmem`` and logs, e.g. ``'sqlite'``.""" 

29 raise NotImplementedError 

30 

31 @abstractmethod 

32 @contextmanager 

33 def connection(self) -> Generator[Any, None, None]: 

34 """Yield a SQLite-API-compatible connection. 

35 

36 Contract: 

37 * Commits on clean exit. 

38 * Rolls back and re-raises on any exception. 

39 * Always closes the underlying connection. 

40 * Rows must support column access by name (``row["column"]``). 

41 """ 

42 raise NotImplementedError 

43 

44 @abstractmethod 

45 def apply_migrations(self, migrations_dir: Path) -> None: 

46 """Run all un-applied numbered ``.sql`` files found in *migrations_dir*. 

47 

48 Implementations must be idempotent — already-applied versions (tracked in 

49 ``schema_migrations``) must be silently skipped. 

50 """ 

51 raise NotImplementedError 

52 

53 def export_snapshot(self, dest: Path) -> None: 

54 """Export a point-in-time snapshot to *dest*. 

55 

56 Optional hook for backup/DR workflows (e.g. Fly.io volume snapshots). 

57 Raise ``NotImplementedError`` if the backend does not support it. 

58 """ 

59 raise NotImplementedError(f"{type(self).__name__} does not support snapshot export") 

60 

61 def import_snapshot(self, src: Path) -> None: 

62 """Restore from a snapshot at *src*. 

63 

64 Optional hook. Raise ``NotImplementedError`` if unsupported. 

65 """ 

66 raise NotImplementedError(f"{type(self).__name__} does not support snapshot import")