
New patches:

[db-postmodern: small sync cache fix
alex.mizrahi@gmail.com**20080318155129
 cache was discarded in empty txn
] {
hunk ./src/db-postmodern/pm-cache.lisp 147
-(defmethod value-cache-commit :around ((w cache-update-wrapper))
+(defmethod value-cache-commit ((w cache-update-wrapper))
}

[db-postmodern: transaction retry handling, serializable isolation
alex.mizrahi@gmail.com**20080318155444] {
hunk ./src/db-postmodern/pm-transaction.lisp 29
+(defun execute-transaction-one-try (sc txn-fn always-rollback)
+  (let (tran commited
+	(*txn-value-cache* (make-value-cache sc)))
+    (incf (tran-count-of sc))
+    (setf tran (controller-start-transaction sc))
+    (unwind-protect
+	 (multiple-value-prog1 	     
+	     (funcall txn-fn) 
+	   (unless always-rollback ; automatically commit unless always-rollback is on
+	     (controller-commit-transaction sc tran)
+	     (setf commited t)))
+      (unless commited (controller-abort-transaction sc tran))
+      (decf (tran-count-of sc)))))
+
+(defmacro with-concurrency-errors-handler (&body body)
+  "execute body with a handler catching postgres concurrency errors 
+   and invoking restart-transaction restart automatically"
+  `(handler-bind
+    ((cl-postgres:database-error
+      (lambda (c)
+	(let ((err-code (cl-postgres:database-error-code c)))
+	  (when (or (string= err-code "40001") ; SERIALIZATION FAILURE
+		    (string= err-code "40P01")); DEADLOCK DETECTED
+	    (invoke-restart 'retry-transaction c))))))
+    ,@body))
+
merger 0.0 (
hunk ./src/db-postmodern/pm-transaction.lisp 56
-				&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 56
-				&key (always-rollback nil) &allow-other-keys)
-  ;; SQL doesn't support nested transaction
+				&key (always-rollback nil) 
+				(retry-cleanup-fn nil)
+				(retries 10) &allow-other-keys)
)
merger 0.0 (
hunk ./src/db-postmodern/pm-transaction.lisp 100
-                         (go retry)))))))))))
+                         (go retry))
+
+                        ((string= (cl-postgres:database-error-code e)
+                                  "25P02")
+                         (warn "25P02: Transaction aborted; something wasn't handled correctly!")
+                         'ignoring-this-error)
+
+                        (t (error e)))))))))))
+
+#|
+
+Notes on error handling:
+
+  40P01: the correct way to handle a detected deadlock is restarting aborted
+         transactions until the locks are resolved. (Leslie)
+
+  23505: this occurs due to a race condition we can't really prevent since
+         it's caused by PL/PGSQL code. Rollback until all races are resolved.
+         A more elegant solution will involve savepoints. (Leslie)
+
+  42P05: another race condition, this time for statements preparation.
+         Same solution. (Leslie)
+
+|#
merger 0.0 (
hunk ./src/db-postmodern/pm-transaction.lisp 94
-                        ((string= (errno e) "40P01")
+                        ((or (string= (cl-postgres:database-error-code e) "40P01")  ; deadlock
+                             (string= (cl-postgres:database-error-code e) "23505")  ; duplicate primary key
+                             (string= (cl-postgres:database-error-code e) "42P05")) ; prepared stmt exists
merger 0.0 (
hunk ./src/db-postmodern/pm-transaction.lisp 91
-                    (dbpm-error (e)
-                      (warn "dbpm txn manager: caught error ~A~%" (errno e))
+                    (cl-postgres:database-error (e)
+                      (warn "dbpm txn manager: caught error ~A~%" (cl-postgres:database-error-code e))
merger 0.0 (
hunk ./src/db-postmodern/pm-transaction.lisp 59
-    (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)))))))))))
hunk ./src/db-postmodern/pm-transaction.lisp 60
-        (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)))))))
+
+	;; SQL doesn't support nested transaction
+	;; TODO: perhaps it's worth detecting abnormal exit here 
+	;; and abort parent transaction too.	
+	(with-concurrency-errors-handler (funcall txn-fn))
+
+	(loop named txn-retry-loop
+	  ;; NB: it does (1+ retries) attempts, 1 try + retries.
+	  for try from retries downto 0 
+	  do (block txn-block
+	       (restart-bind ((retry-transaction
+			       (lambda (&optional condition) 
+				 (when (and retry-cleanup-fn 
+					    (not (= try 0))) ; cleanup is skipped when we are exiting
+				   (funcall retry-cleanup-fn condition sc))
+				 (return-from txn-block))
+			       :report-function (lambda (s) (princ "retry db-postmodern transaction" s)))
+			      (abort-transaction 
+			       (lambda () (return-from txn-retry-loop))))
+			     (with-concurrency-errors-handler 
+			       (return-from txn-retry-loop
+				 (execute-transaction-one-try sc txn-fn always-rollback)))))
+	  finally (error 'transaction-retry-count-exceeded
+			 :format-control "Transaction exceeded the ~A retries limit"
+			 :format-arguments (list retries)
+			 :count retries)))))
+
)
)
)
)
hunk ./src/db-postmodern/pm-transaction.lisp 78
-      (postmodern:execute "BEGIN")
+      (postmodern:execute "BEGIN ISOLATION LEVEL SERIALIZABLE")
}

[transaction restart support plus extended features
alex.mizrahi@gmail.com**20080326203005] {
hunk ./src/db-postmodern/pm-transaction.lisp 56
-				&key (always-rollback nil) &allow-other-keys)
-  ;; SQL doesn't support nested transaction
+				&key (always-rollback nil) 
+				(retry-cleanup-fn nil)
+				(retries 10) &allow-other-keys)
hunk ./src/db-postmodern/pm-transaction.lisp 61
-        (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)))))))
+
+	;; SQL doesn't support nested transaction
+	;; TODO: perhaps it's worth detecting abnormal exit here 
+	;; and abort parent transaction too.	
+	(with-concurrency-errors-handler (funcall txn-fn))
+
+	(loop named txn-retry-loop
+	  ;; NB: it does (1+ retries) attempts, 1 try + retries.
+	  for try from retries downto 0 
+	  do (block txn-block
+	       (restart-bind ((retry-transaction
+			       (lambda (&optional condition) 
+				 (when (and retry-cleanup-fn 
+					    (not (= try 0))) ; cleanup is skipped when we are exiting
+				   (funcall retry-cleanup-fn condition sc))
+				 (return-from txn-block))
+			       :report-function (lambda (s) (princ "retry db-postmodern transaction" s)))
+			      (abort-transaction 
+			       (lambda () (return-from txn-retry-loop))))
+			     (with-concurrency-errors-handler 
+			       (return-from txn-retry-loop
+				 (execute-transaction-one-try sc txn-fn always-rollback)))))
+	  finally (error 'transaction-retry-count-exceeded
+			 :format-control "Transaction exceeded the ~A retries limit"
+			 :format-arguments (list retries)
+			 :count retries)))))
}

[test concurrency extended
alex.mizrahi@gmail.com**20080326203147] {
hunk ./tests/testconcurrency.lisp 4
-;(in-suite* testthreads :in elephant-tests)
+(in-suite* testthreads)
hunk ./tests/testconcurrency.lisp 8
+;;; Alex Mizrahi <alex.mizrahi@gmail.com> 2008
hunk ./tests/testconcurrency.lisp 19
+;;; These tests imply quite strict levels of atomicity and transaction isolation:
+;;; in no way concurrent transactions should affect each other, and in no way information
+;;; should be lost due to concurrent updates.
hunk ./tests/testconcurrency.lisp 23
-(defpclass zork ()
-  ((slot1 :accessor slot1 :initarg :slot1 :initform nil :index t)
-   (slot2 :accessor slot2 :initarg :slot2 :initform nil :index t)))
+(defvar *zork-count* 10)
hunk ./tests/testconcurrency.lisp 25
+(defun setup-zork (&key initially-zero (zork-count *zork-count*) (with-indices t))
+  (wipe-class 'zork)
hunk ./tests/testconcurrency.lisp 28
-; A basic simulation of a web application using Elephant
-; This is also a test showing whether database connections get cleaned up
-; correctly.
+  (if with-indices 
+      (defpclass zork ()
+	((slot1 :initarg :slot1 :initform 0 :index t)
+	 (slot2 :initarg :slot2 :initform 0 :index t)))
+      (defpclass zork ()
+	((slot1 :initarg :slot1 :initform 0)
+	 (slot2 :initarg :slot2 :initform 0))))
+  
+  (loop for i from 0 below zork-count
+	collect (let ((v (if initially-zero 0 i)))
+		  (make-instance 'zork :slot1 v :slot2 v))))
+
+(defun report-retry (condition sc)
+  sc
+  (format t "retrying txn due to:~a~%" condition))
+
+(defmacro do-threaded-tests ((&key (thread-count 5))
+			     &body body)
+  "run computation being tested in multiple threads. *store-controller* gets automatically propagated.
+   thread-count threads will be spawned. 
+   returns two values -- first is one of errors occured in threads, or NIL if there were none.
+   second is a list of results OR errors.
+
+   thread-id from range [0, thread-count) is available inside thread body."
+  `(let ((_sc *store-controller*) ; save controller into lexical var so closure can capture it
+	 (_cv (bt:make-condition-variable))
+	 (_lock (bt:make-lock))
+	 _results _error)
+    (bt:with-lock-held (_lock)
+      
+      (dotimes (thread-id ,thread-count) ; spawn threads
+	(let ((thread-id thread-id)) ; rebind loop variable so it can be captured in closure
+	  (declare (ignorable thread-id))
+	  (bt:make-thread  
+	   (lambda ()
+	     (let ((*store-controller* _sc)
+		   _my-result _my-error)
+	       (unwind-protect 
+		    (handler-case 
+			(setf _my-result 
+			    (progn ,@body))
+		      ;; should we also look for other conditions?
+		      (serious-condition (_e) (setf _my-error _e)))
+		 (bt:with-lock-held (_lock)
+		   (when _my-error
+		   (setf _my-result _my-error
+			 _error _my-error))
+		   (push _my-result _results)
+		   (bt:condition-notify _cv)
+		   (format t "thread ~a notify sent~%" thread-id))))))))
+      ;; now wait for threads to finish. if condvars are not good it can hang forever :(
+      (loop while (< (length _results) ,thread-count)
+	do (bt:condition-wait _cv _lock)))
+    (values _error _results)))
+  
+(defmacro maybe-report-failure (block-name &body body)
+  "if body returns object, it's reported as fail and execution goes out of block"
+  `(let ((error (progn ,@body)))
+    (if error
+	(progn (fail "~a" error)
+	       (return-from ,block-name error))
+	(pass))))
hunk ./tests/testconcurrency.lisp 93
-  (dotimes (i 10)
-    (make-instance 'zork :slot1 i :slot2 i))
+  "test verifies that reads and writes of indexed slots does not yield errors and are consistent.
+ aditionally verifies transactional consistency of threaded operations."
+  (block check-block
+    (setup-zork)
+
+    (dotimes (batch 3) ; run 3 batches
+      (maybe-report-failure check-block
+	(do-threaded-tests (:thread-count 5)
+	  (dotimes (i 3) ; 3 runs
+	    (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)
+	      (ele:with-transaction (:retry-cleanup-fn #'report-retry)
+		;; check if obj can be found via index read
+		(unless (member obj (get-instances-by-value 'zork 'slot1 (slot-value obj 'slot1)))
+		  (error "Failed to find object via index")) ; 5am does not work in threads so we are using ad-hoc constructs
+		
+		(get-instance-by-value 'zork 'slot2 (slot-value obj 'slot2)) ; just check it does not signal errors
+		
+		(unless (= (slot-value obj 'slot1) (slot-value obj 'slot2))
+		  (error "slot1 and slot2 are not equal in zork: ~a and ~a" (slot-value obj 'slot1) (slot-value obj 'slot2)))
+		
+		(setf (slot-value obj 'slot1) (random 50000)
+		      (slot-value obj 'slot2) (slot-value obj 'slot1))))))))
+
+    (let ((zorks (elephant::get-instances-by-class 'zork)))
+      (is (= 10 (length zorks)))
+      (dolist (z zorks)
+	(ele:with-transaction ()
+	  (is (= (slot-value z 'slot1) (slot-value z 'slot2))) ; verify that both slots were transactionally updated
+	  
+	  ;; now verify that indices were not damaged -- retrieve zorks from indices and veryify that all is well.
+	  (let ((same-zorks1 (ele:get-instances-by-value 'zork 'slot1 (slot-value z 'slot1)))
+		(same-zorks2 (ele:get-instances-by-value 'zork 'slot2 (slot-value z 'slot2))))
+	    (is (member z same-zorks1))
+	    (is (member z same-zorks2))
+	    (loop for z1 in same-zorks1 do (is (= (slot-value z1 'slot1) (slot-value z 'slot1))))
+	    (loop for z2 in same-zorks2 do (is (= (slot-value z2 'slot2) (slot-value z 'slot2))))))))))
+
+(test threaded-increments
+  "performs slot increments in multiple threads and verifies that all increments get applied"
+  (block check-block
+    (loop for with-indices in (list nil t)
+	  do (let ((zorks (setup-zork :initially-zero t :with-indices with-indices)))
+	       (maybe-report-failure check-block
+		 (do-threaded-tests (:thread-count 5)
+		   (dotimes (run 5)
+		     (dolist (z zorks)
+		       (ele:with-transaction ()
+			 (unless (= (slot-value z 'slot1) (slot-value z 'slot2))
+			   (error "zork slots values do not match (pre)"))
+			 (incf (slot-value z 'slot1))
+			 (incf (slot-value z 'slot2))
+			 (unless (= (slot-value z 'slot1) (slot-value z 'slot2))
+			   (error "zork slots values do not match (post)")))))))
+
+	       ;; verify that each zork slot was incremented to 25 (5 runs * 5 threads)
+	       (loop for z in zorks
+		     do (is (= 25 (slot-value z 'slot1))) 
+		     do (is (= 25 (slot-value z 'slot2))))))))
+
+(test (threaded-idx-hardcore :depends-on threaded-idx-access)
+  "abusive number of threads (30) simultaneously trying to update poor single slot.
+ formely known as 'provoke-deadlock'"
+  (block check-block
+    (setup-zork :zork-count 1)
+  
+    (maybe-report-failure check-block
+      (do-threaded-tests (:thread-count 30)
+	(let ((obj (car (get-instances-by-class 'zork))))
+	  (setf (slot-value obj 'slot1) 42))))
+    t))
+
+(test cross-update-deadlock
+  "update slots in criss-cross manner from two threads. 
+should yield deadlock which should be retried."
+
+  (block check-block
+    (loop for with-indices in (list nil t)
+	  do (let ((z (first (setup-zork :zork-count 1 :with-indices with-indices))))
+	       (maybe-report-failure check-block 
+		 (do-threaded-tests (:thread-count 2)
+		   (with-transaction (:retry-cleanup-fn #'report-retry)
+		     (cond
+		       ((= thread-id 0) 
+			(setf (slot-value z 'slot1) 2)
+			(sleep 1)
+			(setf (slot-value z 'slot2) 4))
+		       ((= thread-id 1) 
+			(sleep 0.5)
+			(setf (slot-value z 'slot2) 7
+			      (slot-value z 'slot1) 3))))))
+	       
+	       (let ((s1 (slot-value z 'slot1))
+		     (s2 (slot-value z 'slot2)))
+		 (is-true (or (and (= s1 2) (= s2 4))
+			      (and (= s1 3) (= s2 7)))))))))
+     
+(test (threaded-random-order-increments :depends-on (and threaded-increments cross-update-deadlock))
+  "update zorks in random order. this can produce deadlocks."
+  (block check-block
+    (loop for with-indices in (list nil t)
+	  do (let ((zorks (setup-zork :initially-zero t :with-indices with-indices)))
+	       
+	       (maybe-report-failure check-block
+		 (do-threaded-tests (:thread-count 5)
+		   (dotimes (run 5)
+		     (with-transaction (:retry-cleanup-fn #'report-retry)
+		       (dotimes (i 3)
+			 (incf (slot-value
+				(nth (random 10) zorks) ; pick random zork
+				'slot1))
+			 (incf (slot-value
+				(nth (random 10) zorks) ; pick random zork
+				'slot2)))))
+		   (format t "thread ~a finished~%" thread-id)))
hunk ./tests/testconcurrency.lisp 210
-  (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!~%"))
+	       (is (= (* 5 5 3 2)
+		      (loop for z in zorks
+			    summing (+ (slot-value z 'slot1)
+				       (slot-value z 'slot2)))))))))
hunk ./tests/testconcurrency.lisp 215
-  (drop-instances (get-instances-by-class 'zork))
-  (format t "test finished!~%"))
+(defun test-threaded-object-creation (stable-bootstrap indexed) 
+  (block check-block
merger 0.0 (
hunk ./tests/testconcurrency.lisp 218
+#-sbcl
hunk ./tests/testconcurrency.lisp 218
-(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))
+    (setup-zork :zork-count (if stable-bootstrap 1 0) :initially-zero t)
+    
+    (maybe-report-failure check-block
+      (do-threaded-tests (:thread-count 10)
+	(make-instance 'zork :slot1 (1+ thread-id) :slot2 (1+ thread-id))))
)
hunk ./tests/testconcurrency.lisp 223
-  (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.~%"))))
+    (is (= (if stable-bootstrap 11 10)
+	   (length (get-instances-by-class 'zork))))
+    (when indexed
+      (loop for i from (if stable-bootstrap 0 1) to 10
+	    for found = (get-instance-by-value 'zork 'slot1 i)
+	    do (is-true found)
+	    do (is (= i (slot-value found 'slot1)))))))
hunk ./tests/testconcurrency.lisp 231
-  (drop-instances (get-instances-by-class 'zork)))
+(test threaded-object-creation-1 
+  "test creation of objects in threads: with initial object, w/o indices"
+  (test-threaded-object-creation t nil))
hunk ./tests/testconcurrency.lisp 235
+(test (threaded-object-creation-1-i :depends-on threaded-object-creation-1)
+  "test creation of objects in threads: with initial object, w/indices"
+  (test-threaded-object-creation t t))
+#|
+(test threaded-object-creation-0
+  "test creation of objects in threads: w/o initial object, w/o indices"
+  (test-threaded-object-creation nil nil))
hunk ./tests/testconcurrency.lisp 243
+(test (threaded-object-creation-0-i :depends-on threaded-object-creation-0)
+  "test creation of objects in threads: w/o initial object, w/indices"
+  (test-threaded-object-creation nil t))
+|#
}

[db-postmodern: sync-cache type handling fix
alex.mizrahi@gmail.com**20080326222018] {
hunk ./src/db-postmodern/pm-cache.lisp 104
-(defmethod cache-set-value ((cache hash-table) (id integer) key value)
+;; only string or integer can act as key. rest is ignored for now to avoid difficulties 
+;; with type handling.
+
+(defmethod cache-set-value ((cache hash-table) (id integer) (key string) value)
+  (setf (gethash (cons id key) cache) value))
+
+(defmethod cache-set-value ((cache hash-table) (id integer) (key integer) value)
hunk ./src/db-postmodern/pm-cache.lisp 254
-			do (cache-clear-value (parent-cache cache) id key))))))
+			;; information about type was lost, so if key _looks like_ number,
+                        ;; try converting it to number and deleting that key too.
+			for int-key = (when (and (digit-char-p (char key 0))
+						 (digit-char-p (char key (1- (length key)))))
+					(ignore-errors (parse-integer key)))					  
+			do (cache-clear-value (parent-cache cache) id key)
+			when int-key
+			do (cache-clear-value (parent-cache cache) id int-key))))))
+
}

[un-disabled tests for SBCL
alex.mizrahi@gmail.com**20080410070805] {
hunk ./tests/testconcurrency.lisp 91
-#-sbcl
hunk ./tests/testconcurrency.lisp 217
-(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))
+    (setup-zork :zork-count (if stable-bootstrap 1 0) :initially-zero t)
+    
+    (maybe-report-failure check-block
+      (do-threaded-tests (:thread-count 10)
+	(make-instance 'zork :slot1 (1+ thread-id) :slot2 (1+ thread-id))))
}

Context:

[Disabling threading tests for SBCL
Robert L. Read**20080410015544] 
[Chun Tian's conditional for lispworks slot-definition-allocation
sross@common-lisp.net**20080416161010] 
[spelling errors
Robert L. Read**20080408140049] 
[Changed erroneous statement in tutorial that index comparison uses EQUALP.
polzer@gnu.org**20080226123252] 
[DB-POSTMODERN: remove DBPM-ERROR; don't attempt to remove an already prepared statement (pointless since the txn is aborted at the time); defer all errors to txn handler (but warn and print the offending statement)
polzer@gnu.org**20080317171254] 
[DB-POSTMODERN: support transaction retries; handle deadlock; add savepoint utility functions; add warnings to help debugging problematic conditions.
polzer@gnu.org**20080306124528] 
[added BORDEAUX-THREADS dependency and changed PM controller to use it instead of SB-THREAD stuff.
polzer@gnu.org**20080306124512] 
[added concurrency test cases.
polzer@gnu.org**20080306124407] 
[DB-POSTMODERN: reap old connections when a new one is requested.
polzer@gnu.org**20080227150322] 
[Check for unbound slot; potential fix for a compiler error
eslick@common-lisp.net**20080226195839] 
[Fix test dependence for ff-index-test
eslick@common-lisp.net**20080226151654] 
[Improve berkeley DB version agnostic code
eslick@common-lisp.net**20080226151453
 
 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
eslick@common-lisp.net**20080226150114] 
[db-bdb updated to BerkeleyDB 4.6
kazennikov@gmail.com**20071230140731
 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)
polzer@gnu.org**20080222081256] 
[Refactor UTF{16,32}LE serializers.
polzer@gnu.org**20080222084824] 
[Enable multiple process connections to a BDB data-store via DB_REGISTER option
eslick@common-lisp.net**20080225222713] 
[Enable multi-store indexed classes
eslick@common-lisp.net**20080223184504] 
[Change semantics of transaction :retry-count from tries to retries
eslick@common-lisp.net**20080221031015] 
[Minor edits, fixed a comment, fixed a db-lisp out of date error
eslick@common-lisp.net**20080221024151] 
[Alex's patch for 8.3
read@robertlread.net**20080217223512
 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
read@robertlread.net**20080215135054] 
[controller-doc-improvement
read@robertlread.net**20080210155716] 
[tutorial
read@robertlread.net**20080203161532] 
[moved cache-instance into initial-persistent-setup
alex.mizrahi@gmail.com**20080120142436
 because it was bypassed by recreate-instance otherwise
] 
[accessor name in tests change
alex.mizrahi@gmail.com**20080116222405] 
[db-postmodern: pm-btree initialization fixed
alex.mizrahi@gmail.com**20080116222316] 
[recreate-instance stuff improved
alex.mizrahi@gmail.com**20080116220138] 
[Fix instance deserialization to bypass initialization protocol
sross@common-lisp.net*-20071214141938] 
[non-keyword-accessors
sross@common-lisp.net**20080113173616
 allows lispworks to run tests.
] 
[function-call-key-form
sross@common-lisp.net**20080113173547] 
[documentation type fix
read@robertlread.net**20080111151124] 
[Fix the use of internal symbol of sb-kernel in memutils
Leonardo Varuzza <varuzza@gmail.com>**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
kazennikov@gmail.com**20071230141055] 
[db-postmodern: removed specialized map-index
alex.mizrahi@gmail.com**20080107134012
 because pure cursor version works fine and is more robust
] 
[cursor-duplicate removed from db-postmodern
Henrik Hjelte<henrik@evahjelte.com>*-20071124163701] 
[db-postmodern removed possiblity of using NIL as a key in btrees
Henrik Hjelte<henrik@evahjelte.com>**20071124163828] 
[cursor-duplicate removed from db-postmodern
Henrik Hjelte<henrik@evahjelte.com>**20071124163701] 
[Ensure set-db-synch is defined before pset is loaded
sross@common-lisp.net**20071214145041] 
[Fix instance deserialization to bypass initialization protocol
sross@common-lisp.net**20071214141938] 
[db-postmodern fix map-index optimization bug
Henrik Hjelte <henrik.hjelte@stix.to>**20080104151644] 
[db-postmodern: cursors re-implemented
alex.mizrahi@gmail.com**20071215191805] 
[db-postmodern: optimized form-slot-key for persistent-slot-reader
alex.mizrahi@gmail.com**20071207200835
 it uses SBCL internal function now, for other implementation it's less optimized.
] 
[db-postmodern: small example update
alex.mizrahi@gmail.com**20071207200630] 
[db-postmodern: optimized map-index for -by-value case
alex.mizrahi@gmail.com**20071207195402] 
[Fix to from-end traversal of new map-index
eslick@common-lisp.net**20071130223524] 
[New map-index implementation
eslick@common-lisp.net**20071130222620] 
[Cheaper get-instance-by-value
eslick@common-lisp.net**20071130222520] 
[removed a little compiler warning (typo)
Henrik Hjelte<henrik@evahjelte.com>**20071122151929] 
[remove kind-hints parameter from add-index
Henrik Hjelte<henrik@evahjelte.com>**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.
] 
[TAG ELEPHANT-0-9-1
ieslick@common-lisp.net**20071116153634] 
[Fixes to enable the docs to build (on OS X / SBCL using 'make' in elephant/doc)
eslick@common-lisp.net**20071104204802] 
[a little comment update
Henrik Hjelte<henrik@evahjelte.com>**20071106080259] 
[postmodern removed ugly-fix from pm-btree-index 
Henrik Hjelte<henrik@evahjelte.com>**20071106080216
 and made char-columns hardcoded (removed other option).
] 
[random test for serializer
Henrik Hjelte<henrik@evahjelte.com>**20071101144320] 
[POSTMODERN-tests include hints to configure postgres
Henrik Hjelte<henrik@evahjelte.com>**20071101102627] 
[db-postmodern fixed buggy cursor-delete fix secondary-cursor
Henrik Hjelte<henrik@evahjelte.com>**20071101100700] 
[postmodern remove obsolete comment about weak tables
Henrik Hjelte<henrik@evahjelte.com>**20071031023318] 
[postmodern texinfo file
Henrik Hjelte<henrik@evahjelte.com>**20071030185853] 
[db-postmodern update the ugly map-index quick fix
Henrik Hjelte<henrik@evahjelte.com>**20071030181310] 
[db-postmodern secondary cursor should be closed after removing values
Henrik Hjelte<henrik@evahjelte.com>**20071030181154] 
[Postmodern backend: connection spec now accepts :port keyword argument, to specify the port. Similar to Postmodern's connection spec syntax.
tjg@pentaside.org**20071024152639] 
[Fix some test harness issues for lispworks
eslick@common-lisp.net**20071028225431] 
[Fix a typo
eslick@common-lisp.net**20071028223403] 
[Fix signaling test to bind error appropriate for the given lisp
eslick@common-lisp.net**20071028192553] 
[Patch to use core lisp comparison predicates, including fixes to sql cursors and removing a test that works by accident under BDB due to the inability to compare standard objects
eslick@common-lisp.net**20071028191358] 
[Fix bugs that showed up in migration test suite; some test harness detritus and a bug in the SQL txn handling implementation
eslick@common-lisp.net**20071024025205] 
[(#18) Preliminary migration-oriented GC, very slow. Also added warning print vars and did some preliminary work on (#48)
eslick@common-lisp.net**20071024010932] 
[(#40) Allow delete while mapping; add tests; fix more test dependencies; fix bug in map-index
eslick@common-lisp.net**20071023031831] 
[(#19) Fixed increment cursor on cursor-put
eslick@common-lisp.net**20071023004356] 
[Fix bugs in recent changes and tests for change class and character indexing; tests are green
eslick@common-lisp.net**20071023003026] 
[(#7) Delete slot data during instance edits in change-class and redefine-class; optional warning conditions
eslick@common-lisp.net**20071022235935] 
[Fix a defaults bug in manual transaction handling
eslick@common-lisp.net**20071022235855] 
[Fixed a bug in cursor-prev and added a test for standard btree cursors (was missing! wow!)
eslick@common-lisp.net**20071022212318] 
[Add test for characters as index keys
eslick@common-lisp.net**20071022194149] 
[Fix character comparison in BDB data store and lisp-compare functions
eslick@common-lisp.net**20071022162238] 
[Fixed mop test dependencies for fiveam conversion
eslick@common-lisp.net**20071022140438] 
[Fix lisp comparisons for map-index to mirror 0.9p1
eslick@common-lisp.net**20071022135848
 
 Forgot to push patch to lisp-compare<=/=/< functions from
 0.9 CVS to 091 darcs.  Fixed in merge branch. 
 
] 
[Fix FiveAM test dependencies, some Allegro issues, some mis-merges, etc.  See diff for details.
eslick@common-lisp.net**20071019213723] 
[resolve merge conflicts between eslick working branch and postmodern branch
eslick@common-lisp.net**20071019160331] 
[Most recent edits; small bug fixes and query testing
ieslick@common-lisp.net**20071019153850] 
[Add test for unicode values in slots from 0.9p1
ieslick@common-lisp.net**20070627145755] 
[Enable UTF32le unicode support for OpenMCL 1.1
ieslick@common-lisp.net**20070627143750] 
[Pset wasn't persistent
ieslick@common-lisp.net**20070612135351] 
[Last CVS branch bug fix: utf16 serializer
ieslick@common-lisp.net**20070606170001] 
[Keep to date with CVS for release tag 0.9
ieslick@common-lisp.net**20070531195635] 
[First pass complete cl-prevalence backend; still a few bugs to work out
ieslick@common-lisp.net**20070509001453] 
[Cleanup and export instance caching so data stores can override the protocol
ieslick@common-lisp.net**20070509001327] 
[Update elephant code version to 0.9.1
ieslick@common-lisp.net**20070509001216] 
[Cleanup persistent object printing to handle unbound oids
ieslick@common-lisp.net**20070509001123] 
[Simple augmentation of debugging model in deserializer
ieslick@common-lisp.net**20070507031150] 
[Fix map-legacy-names bug for null case
ieslick@common-lisp.net**20070507031026] 
[Prevalence fixes for duplicate and get cursor operations
ieslick@common-lisp.net**20070505174213] 
[Test duplicate operations without depending on primary key ordering part 1
ieslick@common-lisp.net**20070505170910] 
[A tiny convenience.
rread@common-lisp.net**20070911160015] 
[Cleaning up some type declarations
rread@common-lisp.net**20070911155928] 
[Somehow this was fixed before, and then regressed again in the 
rread@common-lisp.net**20070911155714
 current code.  I have added a test which actually exercises,
 according the the XP discipline.  It is a very inelegant test,
 but it is a hard to exercise problem.
] 
[Made enable-sync-cache more efficient and safe
Henrik Hjelte<henrik@evahjelte.com>**20070926015756] 
[db-postmodern ignore errors around close cursor
Henrik Hjelte<henrik@evahjelte.com>**20070921053113] 
[added sh script for flushing logs sample
alex.mizrahi@gmail.com**20070920095806] 
[fixes in pm-cache
alex.mizrahi@gmail.com**20070920071646] 
[global-sync-cache
alex.mizrahi@gmail.com**20070919113321] 
[TAG PostmodernImprovements
ieslick@common-lisp.net**20070822200524] 
[un-disabled instance caching
alex.mizrahi@gmail.com**20070913141409] 
[txn btree value cache
alex.mizrahi@gmail.com**20070912191553] 
[pm-btree make-plpgsql-insert/update duplicates handling fixed
alex.mizrahi@gmail.com**20070905124306] 
[fix type declaration in get-instances-by-range
alex.mizrahi@gmail.com**20070904123721] 
[intern to proper package in make-derived-name
alex.mizrahi@gmail.com**20070904123559] 
[db-postmodern safe-ignore-postgres-error on create-language
Henrik Hjelte<henrik@evahjelte.com>**20070823153914] 
[do-test-spec jumps into debugger by default
Henrik Hjelte<henrik@evahjelte.com>**20070823145758] 
[db-postmodern do not use postmoderns connection pooling
Henrik Hjelte<henrik@evahjelte.com>**20070823094751] 
[db-postmodern remove meaningless function
Henrik Hjelte<henrik@evahjelte.com>**20070823094715] 
[db-postmodern rename with-conn with-postmodern-conn
Henrik Hjelte<henrik@evahjelte.com>**20070823091456] 
[db-postmodern create-language on initialization (Robert L. Read)
Henrik Hjelte<henrik@evahjelte.com>**20070823090504] 
[db-postmodern ignore-errors changed to handler-case
Henrik Hjelte<henrik@evahjelte.com>**20070823085526] 
[db-postmodern update in initialization code
Henrik Hjelte<henrik@evahjelte.com>**20070823085018] 
[db-postmodern bugfix with transaction handling
Henrik Hjelte<henrik@evahjelte.com>**20070823084932] 
[db-postmodern attempt to solve uncommon error 42P01 with prepared statements
Henrik Hjelte<henrik@evahjelte.com>**20070823084724] 
[changed test for serializer
Henrik Hjelte<henrik@evahjelte.com>**20070822020617] 
[db-postmodern: execute-transaction does not use magic macro
Henrik Hjelte<henrik@evahjelte.com>**20070820201508] 
[db-postmodern print-object on cursor
Henrik Hjelte<henrik@evahjelte.com>**20070814090413] 
[db-postmodern bugfix again map-index patch is always needed
Henrik Hjelte<henrik@evahjelte.com>**20070814090244] 
[db-postmodern print-object on pm-btree
Henrik Hjelte<henrik@evahjelte.com>**20070814081715] 
[db-postmodern ignore-errors when preparing-query
Henrik Hjelte<henrik@evahjelte.com>**20070808093908
 An ugly fix that should be solved at some point
] 
[db-postmodern minor changes
Henrik Hjelte<henrik@evahjelte.com>**20070731073031] 
[db-postmodern make char-columns default
Henrik Hjelte<henrik@evahjelte.com>**20070726054334] 
[db-postmodern some with-trans-and-vars changed to with-vars
Henrik Hjelte<henrik@evahjelte.com>**20070725220146] 
[db-postmodern cursor-set refactoring and bugfix
Henrik Hjelte<henrik@evahjelte.com>**20070725220115] 
[typo fix
Ties Stuij**20070724111443] 
[bugfix cursor-set on empty tree
Henrik Hjelte<henrik@evahjelte.com>**20070722140929] 
[db-postmodern some refactoring
Henrik Hjelte<henrik@evahjelte.com>**20070722113311] 
[db-postmodern misc cleaning up
Henrik Hjelte<henrik@evahjelte.com>**20070722075640] 
[db-postmodern some cleaning up
Henrik Hjelte<henrik@evahjelte.com>**20070722074116] 
[make a fixture for indexing tests
Henrik Hjelte<henrik@evahjelte.com>**20070722070302] 
[db-postmodern fix for problem with map-index
Henrik Hjelte<henrik@evahjelte.com>**20070722061401
 elephant apparently requires btrees with strings as keys
 to be sorted correctly according to lisp-compare<=. This works 
 for cl-sql which sorts things in memory, but not for the
 postmodern design which relies on the database to sort things.
 We need to either change elephant or implement db-postmodern
 differently. This fix changes elephant internals temporarily
 when using the db-postmodern backend. Not pretty.
] 
[some more basic indexing tests
Henrik Hjelte<henrik@evahjelte.com>**20070722060733] 
[testcase pcursor2-on-string
Henrik Hjelte<henrik@evahjelte.com>**20070722060604] 
[db-postmodern new implementation of cursor-set
Henrik Hjelte<henrik@evahjelte.com>**20070722030035
 Solved a bug with map-index for strings.
 Works with testcases but needs cleaning up..
] 
[test larger-indexing-with-string
Henrik Hjelte<henrik@evahjelte.com>**20070721180537
 fails on postmodern, works with bdb and clsql
] 
[two more indexing-basic tests for completeness
Henrik Hjelte<henrik@evahjelte.com>**20070721160313] 
[two more trivial map-index tests
Henrik Hjelte<henrik@evahjelte.com>**20070721153616] 
[removed-kv tests are merged to one test
Henrik Hjelte<henrik@evahjelte.com>**20070721150018
 removed old outcommented code
] 
[testcollections changed to fiveam
Henrik Hjelte<henrik@evahjelte.com>**20070721145633] 
[fiveam make a default testsuite elephant-tests
Henrik Hjelte<henrik@evahjelte.com>**20070721145533] 
[Test framework changed to FiveAM
Henrik Hjelte<henrik@evahjelte.com>**20070721044404] 
[file elephant-tests split into several files
Henrik Hjelte<henrik@evahjelte.com>**20070721022740] 
[pm-indexed-btree, remove cache slot
ties**20070717090830
                                                           
] 
[postmodern disable cache
Henrik Hjelte<henrik@evahjelte.com>**20070714161443
 Because it may cause conflicts when different processes access the same database
] 
[PuttingIndexOnMd5column
rread@common-lisp.net**20070710202059
 I haven't gotten Henrik's approval of this yet, but it seems obvious
 (and my test bear out) that this is the column that should be indexed.
] 
[TryingToSurviveSerializationErrors
rread@common-lisp.net**20070710201826
 This is an attempt to survive serialization errrors.
 Rob may be the only person who ever has these (having 
 a live database since around 0.3), and this method 
 is almost a non-method --- but at least it doesn't 
 make your whole attempt to load a database grind to a
 halt.
] 
[keysize test
rread@common-lisp.net**20070710201629
 This is a test of how big a key can get.  It was introduced when
 debugging the postmodern stuff.  Unfortunately, there is a limit,
 which we should work to overcome.
] 
[db-postmodern some cleaning
Henrik Hjelte<henrik@evahjelte.com>**20070709191312] 
[Fixed Roberts bug with large blobs
Henrik Hjelte<henrik@evahjelte.com>**20070709190719
 Becuause the bob columb in blob had a unique index.
 Now the index is on a md5 value of the bob.
] 
[stress-test make subsets work with sbcl
Henrik Hjelte<henrik@evahjelte.com>**20070709093108] 
[db-postmodern send binary data instead of base64
Henrik Hjelte<henrik@evahjelte.com>**20070709092113] 
[db-postmodern blob table bid column is now a 64 bit integer
Henrik Hjelte<henrik@evahjelte.com>**20070708152711] 
[db-postmodern, treat strings as objects.
Henrik Hjelte<henrik@evahjelte.com>**20070706033714
 To avoid this: Database error 54000: index row size 3172 exceeds btree maximum, 2713
 Values larger than 1/3 of a buffer page cannot be indexed.
 Consider a function index of an MD5 hash of the value, or use full text indexing.
] 
[db-postmodern some cleaning
Henrik Hjelte<henrik@evahjelte.com>**20070706033345] 
[updated install-bdb.sh in henrik/contrib
Henrik Hjelte <hhjelte@common-lisp.net>**20070608144005] 
[btree-exec-prepared rewritten
Henrik Hjelte<henrik@evahjelte.com>**20070706005537] 
[postmodern integration
rread@common-lisp.net**20070704214111] 
[postmodern tests file
Henrik Hjelte <hhjelte@common-lisp.net>**20070511171430] 
[db-postmodern bug fix for cursor-previous et al
Henrik Hjelte <hhjelte@common-lisp.net>**20070511170648] 
[testcollections, cursor-previous variants, tests may cheat
Henrik Hjelte <hhjelte@common-lisp.net>**20070511154905
 
 because of loop for while m.
 It makes the test silently leave as soon as m is false
 despite what the value for p and v is. If the m is moved
 to the always clause everything is tested.
] 
[postmodern cursor-set need to be able to move to next key
Henrik Hjelte <hhjelte@common-lisp.net>**20070511070159] 
[upgrade-btree-type in db-postmodern
Henrik Hjelte <hhjelte@common-lisp.net>**20070510143154
 which fixed a bug shown by test pset
] 
[make Makefile documentation build on sbcl
Henrik Hjelte <hhjelte@common-lisp.net>**20070510132113] 
[make db-postmodern compile and load
Henrik Hjelte <hhjelte@common-lisp.net>**20070510093421] 
[initial import db-postmodern
Henrik Hjelte <hhjelte@common-lisp.net>**20070510080504] 
[Prevalence backend BTree index implementation; all but 11 tests pass (with fake serializer)
ieslick@common-lisp.net**20070505043545] 
[Fix btree-index forbidden ops factorization
ieslick@common-lisp.net**20070507023022] 
[Enable subclass override of controller object cache
ieslick@common-lisp.net**20070507022608] 
[Add specific conditions for errors in the deserializer than implementations can dispatch on; also generalized database error signal is exported
ieslick@common-lisp.net**20070507022025] 
[Remove user file my-config.sexp that should not be repo
ieslick@common-lisp.net**20070505221208] 
[Bug fix for primary key missing in map-index
ieslick@common-lisp.net**20070505043920] 
[Factor our forbidden secondary cursor ops
ieslick@common-lisp.net**20070502191218] 
[Resolve README Conflict
ieslick@common-lisp.net**20070502181707] 
[Prevalence store basic ops + BTrees + some index + some prep for new index architecture
ieslick@common-lisp.net**20070502174250] 
[Initial Import of CVS 0.9.1 RC1
ieslick@common-lisp.net**20070430224026] 
[Empty repository
ieslick@common-lisp.net**20070308220933] 
Patch bundle hash:
7c8bb4598550dc76a2bd40a0a3879c787f0543d3
