Skip to content

symfonic.memory.backends.in_memory_committed

in_memory_committed

Opt-in, graph-only committed reads over the exact InMemory reference.

Serving reads wait for transaction outcome and return detached values. Transaction readback is deliberately private and explicit. Compose every serving graph port over this SAME wrapper instance: raw/wrapped domains are different, and direct backend reads remain outside this contract. This does not authorize Working POST, prove session ownership, or offer crash durability.

CommittedInMemoryGraph

CommittedInMemoryGraph(backend: InMemoryGraphBackend)

Static GraphBackend surface; unknown extensions fail closed.

Single calls are isolated, not a multi-call application snapshot. Wrap a composed read in committed_read() for a stable view. It is read-only; entering a write transaction inside it is rejected instead of deadlocking. Tasks inheriting a read view are refused even after that view exits: start serving tasks outside readback/grouped views. This conservative reference serializes readers as well as writers; it is not a production throughput claim.

Source code in src/symfonic/memory/backends/in_memory_committed.py
def __init__(self, backend: InMemoryGraphBackend) -> None:
    if type(backend) is not InMemoryGraphBackend:
        raise TypeError("committed reads require exact InMemoryGraphBackend")
    # Adding a backend operation requires an explicit classification here.
    unknown = [
        name for name, value in getmembers(type(backend))
        if not name.startswith("_") and callable(value) and name != "transaction"
        and getattr(getattr(type(self), name, None), "_committed_operation", None)
        not in {"read", "write"}
    ]
    if unknown:
        raise TypeError(f"unclassified backend operations: {unknown}")
    self._backend = backend

committed_read async

committed_read()

One same-task read-only view; transaction-local serving is refused.

Source code in src/symfonic/memory/backends/in_memory_committed.py
@asynccontextmanager
async def committed_read(self):
    """One same-task read-only view; transaction-local serving is refused."""
    held = self._view()
    if held is not None:
        yield self
        return
    if _owns(self._backend):
        raise RuntimeError("transaction read requires private_transaction_read")
    inherited = _VIEW.get()
    if (_context_owner(self._backend) or (
        inherited is not None and inherited[0]._backend is self._backend
    )):
        raise RuntimeError("inherited read/transaction context is not serving admission")
    async with writer(self._backend):
        token = _VIEW.set((self, current_task(), False))
        try:
            yield self
        finally:
            _VIEW.reset(token)

confirm_privacy_subtree_absent async

confirm_privacy_subtree_absent(scope: StorageScope) -> bool

Read all graph-owned privacy rows in the committed view, without relocking.

Source code in src/symfonic/memory/backends/in_memory_committed.py
async def confirm_privacy_subtree_absent(self, scope: StorageScope) -> bool:
    """Read all graph-owned privacy rows in the committed view, without relocking."""
    from symfonic.memory.backends.in_memory_ownership import privacy_absent

    async with self.committed_read():
        return privacy_absent(self._backend, scope)

private_transaction_read async

private_transaction_read()

Explicit own-transaction readback, never default serving admission.

An inherited context alone cannot admit a child task. This view must be entered by a task owning this wrapper's transaction, and remains read-only. It intentionally observes its own uncommitted writes.

Source code in src/symfonic/memory/backends/in_memory_committed.py
@asynccontextmanager
async def private_transaction_read(self):
    """Explicit own-transaction readback, never default serving admission.

    An inherited context alone cannot admit a child task. This view must
    be entered by a task owning this wrapper's transaction, and remains
    read-only. It intentionally observes its own uncommitted writes.
    """
    if (_TRANSACTION.get() != (self, current_task())
            or not _owns(self._backend)):
        raise RuntimeError("private read requires this task's wrapper transaction")
    token = _VIEW.set((self, current_task(), True))
    try:
        yield self
    finally:
        _VIEW.reset(token)

transaction async

transaction()

Yield only this wrapper; never expose the underlying backend.

Source code in src/symfonic/memory/backends/in_memory_committed.py
@asynccontextmanager
async def transaction(self):
    """Yield only this wrapper; never expose the underlying backend."""
    self._require_mutable()
    async with self._backend.transaction():
        token = _TRANSACTION.set((self, current_task()))
        try:
            yield self
        finally:
            _TRANSACTION.reset(token)