symfonic.memory.backends.in_memory_transaction¶
in_memory_transaction ¶
All of a batch, or none of it, for the backend that lives in this process.
The Postgres pools offer a transaction and the in-process backend needs the same word for the same promise, because the consolidation commit asks for one by name. "Nothing in the batch awaits anything that yields, so nothing can interleave" was only half the guarantee: a batch whose third operation raises has already applied the first two, and no amount of non-interleaving takes those back.
So the tables are snapshotted on entry and restored if the block raises. Deep,
because update_node mutates the stored node in place -- a shallow copy of
the dicts would leave every field change from the failed block sitting in the
"restored" rows.
Restoring cannot lose a concurrent writer's work: nothing inside the block awaits anything that yields, so there is no concurrent writer during it. That is the part the earlier reasoning got right, and it is what makes snapshot-and-restore safe rather than merely convenient.
Its own module, and imported from inside the method rather than at module
scope, so it stays out of the eager import graph an adopter pays for on
from symfonic.agent import SymfonicAgent. Nothing needs it until a batch
opens a transaction, and only a consolidation commit does.
snapshot
async
¶
Run a block against store so its writes all land or none of them do.
Nested use joins the outer block rather than opening a savepoint: the callers want "all of this together", and an inner block that could commit on its own would be a quieter way of not having a transaction.
Reaches the tables directly because a snapshot of a store's rows is what this is; publishing them so an outsider could copy them would be a wider surface for no gain, since only this function may.