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

Reply via email to