Transaction Example with Rollback?

Hi,

I just re-read some documentation of transactions,
and picked up some keywords:

"Impact of transactions*
… generation … rollback …
https://www.swi-prolog.org/pldoc/man?section=transaction-impact

Would it be possible instead of a transaction/1 braket,
to have a global ongoing transaction which can

  1. have multiple progressive manually set snapshots,
  2. where I can revert step by step to these snapshots?

By some commands from the top-level?

Not sure what you are after. snapshots and transactions are strictly nested. Changing that would be require serious change of the design. So, bracketing them using a goal is not a restriction. On the toplevel you can do this to play around inside a snapshot.

?- snapshot(prolog). 

Thus,

101 ?- snapshot(prolog).
% Break level 1
[1] 102 ?- assert(p(1)).
true.

[1] 103 ?- ^D
% Exit break level 1
true.

102 ?- p(X).
false.

Already SQL delivers a different interface. You have for example
a command ROLLBACK. So for a “single snapshot”, you can
freely decide when to use the command ROLLBACK.

MAKE SOME TABLE INSERT
MAKE SOME TABLE UPDATE
MAKE SOME TABLE DELETE
NOW CHOOSE ROLLBACK

The closest to this interface I found in SWI-Prolog was the library
stomp. Which has stomp_abort/2. Can I have multiple transaction open
in stomp, linearly one after the other, and rollback one or two transactions with the above stomp_abort/2 command? Is there
a memory version of stomp, so that a mockup can be made
without the communication hassle?

Edit 30.06.2022:
If you have primitives begin/0, rollback/0 and commit/0, you can
bootstrap the brakets, maybe you need setup_call_cleanup/3 somewhere
or a once/1 somewhere, but the sketch would be I guess:

snapshot(G) :-
    begin,
    G,
    rollback.

transaction(G) :-
    begin,
    G,
    commit.

transaction/1 possibly needs a catch/3 and a rollback/0 as well?
But the brackets snapshot/1 and transaction/1 are not so useful
to write user interfaces in inverted style.

Inverted style is when a button has a callback and the event loop
is the application master. Do you suggest to call shapshot/1 and
then open a sub event loop, wait for the button,

and then do something? Or asked differently not in the
context of GUIs, but in the context of transactions for lets
say pengines on a remote server:

  • Can I start a transaction by some pengine message?
  • Can I abort a transaction by some pengine message?

STOMP is a network protocol that is implemented by a number of message brokering systems. Being a network protocol, there is no local in memory version. Transactions in STOMP have an identifier, so I guess you can have multiple open, depending on the actual backend.

As for Prolog, transactions are connected to a thread and transactions in the same thread must be strictly nested. That solves the main problems with concurrent updates to the Prolog database, i.e., you can realize consistent and atomic changes to the database that affect multiple predicates.

When using with a GUI, I guess we have one button that starts the operation. We create a thread to run the operation and the “abort” button can do

 thread_signal(Worker, abort).

to abort the ongoing computation and revert any possible changes to the database. For network initiated commands that take some time to run you can do the same.

prolog/0 runs the interactive toplevel. As is, that stops abort/0 and there is no way out except for end-of-file (causing it to succeed) or halt/0. That may be a little too drastic. This is more to demonstrate how you can use goals in threads to scope a transaction and terminate them externally (except for interactive sessions running prolog/0).

This one works, ordinary exit:

?- transaction([user]).
:- assertz(foo(bar)).
:- assertz(foo(baz)).
^D
% user://1 compiled 0.00 sec, 0 clauses
true.
̀?- listing(foo/1).
:- dynamic foo/1.
foo(bar).
foo(baz).
true.

Abort exit, one can see the rollback:

?- transaction([user]).
:- assertz(foo(bar)).
:- assertz(foo(baz)).
:- abort.

% Execution Aborted
?- listing(foo/1).
:- dynamic foo/1.
true.

But I can still not test progressive transactions, a linear
form of nested transactions. This here doesn’t work:

?- transaction([user]).
:- assertz(foo(bar)).
:- assertz(foo(baz)).
:- transaction([user]).
:- retract(foo(bar)).
:- listing(foo/1).
:- dynamic foo/1.
foo(baz).
|:- abort.
% Execution Aborted
?- listing(foo/1).
:- dynamic foo/1.
true.

The abort doesn’t bring me to the previous transaction. It
aborts all transactions. Maybe use a catch/3 around the second
call of transaction? What does [user] inside [user] even do?

Edit 02.07.2022:
In SQL you need not only the ROLLBACK command but
also the SAVE TRANSACTION command. And a nested transaction
needs to be started with the later command to allow partial rollback:

Understanding SQL Server Transaction Savepoints
https://www.mssqltips.com/sqlservertip/5538/understanding-sql-server-transaction-savepoints/