Hello everyone,

I wrote two concurreny test cases, added Bordeaux Threads and revised
the Postmodern error and transaction handling (for example, deadlocks
are no longer a problem).

Also included is the patch to close dangling connections I sent about
two weeks ago.

Please test them if you can spare a minute.

  Thanks!

    Leslie
New patches:

[DB-POSTMODERN: reap old connections when a new one is requested.
[EMAIL PROTECTED] {
hunk ./src/db-postmodern/pm-controller.lisp 71
+
+(defun reap-orphaned-connections (sc)
+  (let ((n-reaped 0))
+    #+sbcl(maphash (lambda (thread bookkeeper)
+               (let ((alive-p (sb-thread:thread-alive-p thread)))
+                 (unless alive-p
+                   (cl-postgres:close-database (car bookkeeper))
+                   (incf n-reaped)
+                   (remhash thread (controller-db-table sc)))))
+             (controller-db-table sc))
+    n-reaped))
+
hunk ./src/db-postmodern/pm-controller.lisp 90
+            (reap-orphaned-connections sc)
}

[added concurrency test cases.
[EMAIL PROTECTED] {
hunk ./elephant-tests.asd 63
+	     (:file "testconcurrency")
addfile ./tests/testconcurrency.lisp
hunk ./tests/testconcurrency.lisp 1
+(in-package :ele-tests)
+
+;; not enabled by default
+;(in-suite* testthreads :in elephant-tests)
+
+;;;
+;;; Leslie P. Polzer <[EMAIL PROTECTED]> 2008
+;;;
+;;; These tests will (as of March 2008) fail horribly on
+;;;   * BDB without deadlock detection enabled.
+;;;   * CLSQL/SQLite
+;;;   * Probably other CLSQL backends
+;;;
+;;; The Postmodern backend should handle them correctly,
+;;; and BDB as well (although I noticed a major slowdown).
+;;;
+
+
+(defpclass zork ()
+  ((slot1 :accessor slot1 :initarg :slot1 :initform nil :index t)
+   (slot2 :accessor slot2 :initarg :slot2 :initform nil :index t)))
+
+
+; A basic simulation of a web application using Elephant
+; This is also a test showing whether database connections get cleaned up
+; correctly.
+
+(test threaded-idx-access
+  (dotimes (i 10)
+    (make-instance 'zork :slot1 i :slot2 i))
+
+  (dotimes (batch 20)
+    (dotimes (i 5)
+      (bt:make-thread (lambda ()
+                        (dotimes (i 5)
+                          (format t "thread ~A: batch ~A, run ~A~%" (bt:current-thread) batch i)
+                          (dolist (obj (elephant::get-instances-by-class 'zork))
+                            (format t "now handling obj ~A~%" obj)
+                            (setf (slot-value obj 'slot1) i)
+                            (setf (slot-value obj 'slot2) (slot-value obj 'slot1)))))))
+    #+sbcl(dolist (thr (bt:all-threads))
+            (format t "waiting for thread ~A to finish...~%" thr)
+            (unless (eq thr (bt:current-thread))
+              (sb-thread:join-thread thr)))
+    (format t "batch finished!~%"))
+
+  (drop-instances (get-instances-by-class 'zork))
+  (format t "test finished!~%"))
+
+(test provoke-deadlock ;; sometimes throws a 23505 (primary key constraint violation)
+                       ;; I have not tracked this down, yet.
+  (dotimes (i 10)
+    (make-instance 'zork :slot1 i :slot2 i))
+
+  (dotimes (i 30)
+    (bt:make-thread
+      (lambda ()
+        (format t "thread no ~A starting~%" i)
+        (let ((obj (car (get-instances-by-class 'zork))))
+          (setf (slot-value obj 'slot1) i)) ;; this only provokes deadlocks when
+                                            ;; the slot in question is indexed.
+        (format t "thread finished.~%"))))
+
+  (drop-instances (get-instances-by-class 'zork)))
+
}

[added BORDEAUX-THREADS dependency and changed PM controller to use it instead of SB-THREAD stuff.
[EMAIL PROTECTED] {
hunk ./elephant.asd 327
-  :depends-on (:uffi :cl-base64))
+  :depends-on (:uffi :cl-base64 :bordeaux-threads))
hunk ./src/db-postmodern/package.lisp 8
-	#+sbcl :sb-thread)
+	:bordeaux-threads)
hunk ./src/db-postmodern/pm-controller.lisp 74
-    #+sbcl(maphash (lambda (thread bookkeeper)
-               (let ((alive-p (sb-thread:thread-alive-p thread)))
+    (maphash (lambda (thread bookkeeper)
+               (let ((alive-p (thread-alive-p thread)))
hunk ./tests/elet-package.lisp 22
-  (:use :common-lisp :elephant :5am)
+  (:use :common-lisp :elephant :5am :bordeaux-threads)
hunk ./tests/elet-package.lisp 24
-  (:use :common-lisp :elephant :regression-test)
+  (:use :common-lisp :elephant :regression-test :bordeaux-threads)
hunk ./tests/elet-package.lisp 60
+
}

[DB-POSTMODERN: support transaction retries; handle deadlock; add savepoint utility functions; add warnings to help debugging problematic conditions.
[EMAIL PROTECTED] {
hunk ./src/db-postmodern/pm-sql.lisp 3
+(define-condition dbpm-error (error)
+    ((errno :reader errno :initarg :errno))
+      (:report (lambda (cond s)
+                 (format s "DB error ~S"
+                         (errno cond)))))
+
hunk ./src/db-postmodern/pm-sql.lisp 155
+(defun set-savepoint (con savepoint)
+  (ignore-errors
+    (cl-postgres:exec-query con (concatenate 'string "SAVEPOINT " savepoint))))
+
+(defun rollback-to-savepoint (con savepoint)
+  (ignore-errors
+    (cl-postgres:exec-query con (concatenate 'string "ROLLBACK TO " savepoint))))
+
hunk ./src/db-postmodern/pm-sql.lisp 181
+                        (warn "42P05: prepared statement already exists!")
+                        'ignoring-this-error)
hunk ./src/db-postmodern/pm-sql.lisp 190
-                         'ignoring-this-error)
hunk ./src/db-postmodern/pm-sql.lisp 201
-        (handler-case
-            (exec-prepared name-string)
-          (cl-postgres:database-error (e)
-            ;; Sometimes the prepared statement might hold references to old oids,
-            ;; which might be have been dropped after a rollback. For safety, try
-            ;; to remove the prepared statement and prepare it again
-            (cond
-              ((string= (cl-postgres:database-error-code e)
-                        "42P01")
-               ;; It seems that this error automatically drops the transaction! Postgresql bug?
-               (cl-postgres:exec-query (active-connection) (concatenate 'string "DEALLOCATE " name-string))
-               (cl-postgres:prepare-query (active-connection) name-string sql)
-               (exec-prepared name-string))
-              (t (error e)))))))))
+        (let ((savepoint (princ-to-string (gensym))))
+          ;(set-savepoint (active-connection) savepoint)
+          (handler-case
+              (progn
+                ;(format t "Executing prepared query ~A~%" name-string)
+                (exec-prepared name-string))
+            (cl-postgres:database-error (e)
+              ;; Sometimes the prepared statement might hold references to old oids,
+              ;; which might be have been dropped after a rollback. For safety, try
+              ;; to remove the prepared statement and prepare it again
+              (warn "Error while executing prepared statement ~S (params: ~A).~%"
+                    name-string params)
+              (cond
+                ((string= (cl-postgres:database-error-code e)
+                          "42P01")
+                 ;; It seems that this error automatically drops the transaction! Postgresql bug?
+                 (warn "42P01: Prepared statement already exists; trying to remove it.") 
+                 ;(rollback-to-savepoint (active-connection) savepoint)
+                 (cl-postgres:exec-query (active-connection) (concatenate 'string "DEALLOCATE " name-string))
+                 (cl-postgres:prepare-query (active-connection) name-string sql)
+                 (exec-prepared name-string))
+
+                ((string= (cl-postgres:database-error-code e)
+                          "40P01") ; deadlock, defer to txn handler
+                 (error 'dbpm-error :errno "40P01"))
+
+                ((string= (cl-postgres:database-error-code e)
+                          "25P02")
+                 (warn "25P02: Transaction aborted; something wasn't handled correctly!")
+                 'ignoring-this-error)
+
+                (t (error e))))))))))
+
hunk ./src/db-postmodern/pm-sql.lisp 330
+
hunk ./src/db-postmodern/pm-transaction.lisp 30
-				&key (always-rollback nil) &allow-other-keys)
-  ;; SQL doesn't support nested transaction
+				&key (retries 50) (always-rollback nil) &allow-other-keys)
hunk ./src/db-postmodern/pm-transaction.lisp 32
-    (if (> (tran-count-of sc) 0)
-        (funcall txn-fn)
-        (let (tran 
-	      commited
-	      (*txn-value-cache* (make-value-cache sc)))
-          (incf (tran-count-of sc))
-          (unwind-protect
-	       (prog2 
-		   (setf tran (controller-start-transaction sc))
-		   (funcall txn-fn) ;;this gets returned
-		 (unless always-rollback ;;automatically commit unless always rollback
-		   (controller-commit-transaction sc tran)
-		   (setf commited t)))
-	    (unless commited (controller-abort-transaction sc tran))
-	    (decf (tran-count-of sc)))))))
+    (let (savepoint (try 0))
+      (tagbody
+        retry (incf try)
+              (when (>= try retries)
+                (cerror "Retry transaction again?"
+		       'transaction-retry-count-exceeded
+		       :format-control "Transaction exceeded the limit of ~A retries"
+		       :format-arguments (list retries)
+		       :count retries))
+              ;(format t "txn-mgr (thr ~A): try ~A~%" sb-thread::*current-thread* try)
+              ;; XXX honor max retries
+              (if (> (tran-count-of sc) 0) ;; SQL doesn't support nested transaction
+                (progn
+                  ;(setf savepoint (princ-to-string (gensym)))
+                  ;(set-savepoint (active-connection) savepoint)
+                  ;(setf savepoint nil)
+                  ;(format t "detected nested transaction~%")
+                  (return-from execute-transaction (funcall txn-fn)))
+                (let (tran)
+                  (handler-case
+                    (let (commited (*txn-value-cache* (make-value-cache sc)))
+                      (incf (tran-count-of sc))
+                      (unwind-protect
+                        (return-from execute-transaction
+                          (prog2 
+                            (setf tran (controller-start-transaction sc))
+                            (funcall txn-fn) ;; this gets returned
+                            (unless always-rollback ;; automatically commit unless always rollback
+                              (controller-commit-transaction sc tran)
+                              (setf commited t))))
+                        (unless commited (controller-abort-transaction sc tran))
+                        (decf (tran-count-of sc))))
+                    (dbpm-error (e)
+                      (warn "dbpm txn manager: caught error ~A~%" (errno e))
+                      (cond
+                        ((string= (errno e) "40P01")
+                         ;(if savepoint
+                         ;(rollback-to-savepoint (active-connection) savepoint)
+                         (controller-abort-transaction sc tran)
+                         (go retry)))))))))))
}

Context:

[Check for unbound slot; potential fix for a compiler error
[EMAIL PROTECTED] 
[Fix test dependence for ff-index-test
[EMAIL PROTECTED] 
[Improve berkeley DB version agnostic code
[EMAIL PROTECTED]
 
 Added an error message to configure my-config.sexp and made sure we load
 it directly from my-config.sexp so that we get it right at load time.
 Prior patch didn't override default until after load time.
 
] 
[Support for multiple BDB versions
[EMAIL PROTECTED] 
[db-bdb updated to BerkeleyDB 4.6
[EMAIL PROTECTED]
 Changed only BDB constants as upgrade 4.5 -> 4.6 they were
 changed.
 A kind of hack perhaps. But it works. The testing was not excessive,
 but it works well for my project.
] 
[add test for STRING types (as opposed to SIMPLE-STRING types)
[EMAIL PROTECTED] 
[Refactor UTF{16,32}LE serializers.
[EMAIL PROTECTED] 
[Enable multiple process connections to a BDB data-store via DB_REGISTER option
[EMAIL PROTECTED] 
[Enable multi-store indexed classes
[EMAIL PROTECTED] 
[Change semantics of transaction :retry-count from tries to retries
[EMAIL PROTECTED] 
[Minor edits, fixed a comment, fixed a db-lisp out of date error
[EMAIL PROTECTED] 
[Alex's patch for 8.3
[EMAIL PROTECTED]
 I entered here the patch from Alex of 2088/02/16
 which apparently makes this compatible with Postgres 8.3.
 It is green for me on all tests on Posgres 8.1, so 
 I am committing it.
] 
[mtype change in dcm
[EMAIL PROTECTED] 
[moved cache-instance into initial-persistent-setup
[EMAIL PROTECTED]
 because it was bypassed by recreate-instance otherwise
] 
[accessor name in tests change
[EMAIL PROTECTED] 
[db-postmodern: pm-btree initialization fixed
[EMAIL PROTECTED] 
[recreate-instance stuff improved
[EMAIL PROTECTED] 
[db-postmodern: removed specialized map-index
[EMAIL PROTECTED]
 because pure cursor version works fine and is more robust
] 
[cursor-duplicate removed from db-postmodern
Henrik Hjelte<[EMAIL PROTECTED]>*-20071124163701] 
[db-postmodern fix map-index optimization bug
Henrik Hjelte <[EMAIL PROTECTED]>**20080104151644] 
[db-postmodern: cursors re-implemented
[EMAIL PROTECTED] 
[controller-doc-improvement
[EMAIL PROTECTED] 
[tutorial
[EMAIL PROTECTED] 
[non-keyword-accessors
[EMAIL PROTECTED]
 allows lispworks to run tests.
] 
[function-call-key-form
[EMAIL PROTECTED] 
[documentation type fix
[EMAIL PROTECTED] 
[Fix the use of internal symbol of sb-kernel in memutils
Leonardo Varuzza <[EMAIL PROTECTED]>**20071230000120
 
 memutil.lisp use the functions sb-kernel::copy-*-from-system-area, which
 aren't exported in the latest version of sbcl.
 
 Fix it adding the :: when appropriate
 
] 
[db-bdb bugfix: when bdb key comparison compared only the first half of utf16 strings
[EMAIL PROTECTED] 
[Fix instance deserialization to bypass initialization protocol
[EMAIL PROTECTED] 
[db-postmodern: optimized map-index for -by-value case
[EMAIL PROTECTED] 
[db-postmodern: optimized form-slot-key for persistent-slot-reader
[EMAIL PROTECTED]
 it uses SBCL internal function now, for other implementation it's less optimized.
] 
[db-postmodern: small example update
[EMAIL PROTECTED] 
[added sh script for flushing logs sample
[EMAIL PROTECTED] 
[db-postmodern removed possiblity of using NIL as a key in btrees
Henrik Hjelte<[EMAIL PROTECTED]>**20071124163828] 
[cursor-duplicate removed from db-postmodern
Henrik Hjelte<[EMAIL PROTECTED]>**20071124163701] 
[removed a little compiler warning (typo)
Henrik Hjelte<[EMAIL PROTECTED]>**20071122151929] 
[remove kind-hints parameter from add-index
Henrik Hjelte<[EMAIL PROTECTED]>**20071122151046
 Probably a coming feature from Ian, but
 right now it breaks the generic function add-index
 and thus postmodern, so I removed it for now.
] 
[Ensure set-db-synch is defined before pset is loaded
[EMAIL PROTECTED] 
[Fix instance deserialization to bypass initialization protocol
[EMAIL PROTECTED] 
[Fix to from-end traversal of new map-index
[EMAIL PROTECTED] 
[New map-index implementation
[EMAIL PROTECTED] 
[Cheaper get-instance-by-value
[EMAIL PROTECTED] 
[TAG ELEPHANT-0-9-1
[EMAIL PROTECTED] 
Patch bundle hash:
3ce64eb4f1020f3d96cab54935d3e7cbb9ae06fd
_______________________________________________
elephant-devel site list
elephant-devel@common-lisp.net
http://common-lisp.net/mailman/listinfo/elephant-devel

Reply via email to