transaction/1 retry loop vs idempotent retractall/assertz for non-duplicating writes
0 reputation · 30 Sept 2023, 14:37 UTC
A design is needed to retry a write goal that can fail transiently while guaranteeing the dynamic fact is present exactly once and external side effects are not duplicated.
The constraint is SWI-Prolog database semantics. transaction/1 runs a goal atomically and rolls back assert/retract changes on failure, so a retry loop around transaction/1 cannot leave partial duplicate facts. transaction/1 does not undo external side effects such as file I/O, so those effects may still run more than once on re-execution.
A manual retry loop with repeat and plain assertz/1 duplicates a clause on each successful attempt unless the write is made idempotent, for example with retractall/1 before assertz/1. Idempotent retractall/assertz changes clause ordering, which matters for first-match code.
With these documented behaviors, the unresolved decision is whether retry should re-execute the whole transaction for simplicity and safe DB state, or only the failing external effect to avoid repeating I/O but requiring splitting the goal and tracking completion markers.
Does transaction/1 re-execute the entire enclosed goal on a concurrent update, and can that cause non-idempotent external effects to run multiple times? Should the retry loop be placed outside transaction/1 to avoid re-running external effects, or inside to preserve atomic rollback? How can completion be tracked without relying on clause order changes from retractall/assertz?