For the record, the current measure of a successful body within
(with-transaction ()) is an ordinary exit. _ANY_ non-local exit such as
throw, error, etc as well as goto labels, or (return-from <function>)
all result in aborts. More technically the test is written as:
(with-transaction ()
<txn-body>)
=>
(unwind-protect
(prog1
<txn-body>
(setf success t)
(db-transaction-commit ...))
(unless success
(db-transaction-abort ...)))
Should this policy be changed? Should all conditions and throws result
in aborts but other non-local exits in success?
First of all, unless I am missing something, shouldn't it be (progn
<txn-body> (db-transaction-commit ...) [and then] (setf success t))?
I think that the policy of aborting the transaction on all non-local
exits as it is now is a good idea. Maybe <txn-body> will funcall some
lambda passed to it that does weird things with the control flow which
could lead to hard-to-find bugs. I also have a feeling this may lead
to such bugs with code that tries to fake continuations in some
instances, but I can't think of any examples. However, then there
should be a protocol to let programmers deal with transactions in
hairy code themselves (hey, transactions are a part of the business
logic or whatever... :)). Here is what I propose:
(defun close-transaction () ;; this gets exported
(db-transaction-commit :transaction *txn*
:txn-nosync *txn-nosync*
:txn-sync *txn-sync*)
(setq *success* t))
now (with-transaction <body>) becomes:
(let ((*success* nil) (*txn ... )
(declare (special *success*) (special *txn... )
(unwind-protect
(progn
<body>
(unless *success* (close-transaction))) ;; this makes sure we
don't commit twice
(unless *success* (abort-transaction... ))
So when you want to do something to leave control from <body> without
reaching the end, you can call (elephant:close-transaction) or
whatever (maybe backend-close-transaction is a better name?) to
explicitly close that transaction. This also handles the problem that
somebody may want to raise an error or something but still want the
transaction to go through.
I would implement the above myself, but I can't get Elephant from the
CVS circa right now to work. First thing is that in serializer.lisp,
in functions get-circularity-hash and release-circularity-hash, there
is the (#+allegro mp::with-process-lock (*circularity-lock*)
(pop/push...)), which SBCL doesn't like, so you have to do #+allegro
(mp::with-process lock... (pop/push...)) #-allegro (pop/push...). Then
in sleepycat.lisp, (def-function ("db_compact" ... should have
:returning :pointer-void (or maybe it is better for def-function
lambda-list to default &key returning to &key (returning
:pointer-void)?). After that everything seems to compile but nothing
seems to work. I'm not quite sure why, and I'm too tired to find out
right now.
Vladimir
_______________________________________________
elephant-devel site list
elephant-devel@common-lisp.net
http://common-lisp.net/mailman/listinfo/elephant-devel