At 11:51 AM +1200 6/1/06, Sam Vilain wrote:
I think the answer lies in the "checkpointing" references in that
document.  I don't know whether that's akin to a SQL savepoint (ie, a
point mid-transaction that can be rolled back to, without committing the
entire transaction) or more like a continuation that when resumed can
see the atomic changes, and when exiting finally applies them (or rolls
back).  Perhaps someone else will have more of a clue.
Sam.

Rather than thinking about "save points", it would be better to think of the problem in terms of "child transactions". (Note that what I'm saying here is simplified to assume there aren't any irreversable actions, but it can easily be extended to handle those situations too.)

Each time a context (a code block, either a routine or a syntactic construct like 'try' is) is entered that is marked 'is atomic', a new transaction begins, which as a whole can later be committed or rolled back; it implicitly commits if that context is exited normally, and it rollsback implicitly if the context exits with a 'fail' and/or due to a thrown exception. (And yes, I see it as being easier to use if rollback and fail are generally joined at the hip.)

One atomic context can contain another atomic context, so they are layered; if an outer layer rolls back, it results in any 'successful' inner layers also rolling back, so that everything which happened within the outer context has rolled back.

If we simply have child atomic contexts to implement sub-transactions of a parent atomic context / transaction, rather than relying on savepoints or whatever, then it is much easier to make reusable or movable or recursive code, since the code doesn't have to specify its own atomicness differently depending on whether its caller is being atomic or not.

Eg, we could have a situation like this:

  sub foo is atomic { ... }

  sub bar is atomic { ... }

  sub baz is atomic { ... }

  sub quux is atomic {
    ...
    foo();
    ...
    try {
      bar();
    }
    catch {
      baz();
    }
    ...
  }

  quux();

All 4 of the above subroutines are individually atomic and will throw an exception / return 'fail' and rollback on failure. If quux() fails or foo() or baz() fail, nothing that either of those 3 subroutines did will persist. If bar() fails, but baz() succeeds, then only what bar() did has rolled back, and the rest persists when quux() returns.

Those are my thoughts concerning transactions. I haven't specifically addressed any matters related to exclusivity of resources in the cases of multiple processes or threads, but they can easily be added onto what I stated, which is also useful when there is just a single process with a single thread.

-- Darren Duncan

Reply via email to