Update of /project/elephant/cvsroot/elephant/src/elephant
In directory clnet:/tmp/cvs-serv24007/src/elephant
Modified Files:
migrate.lisp package.lisp
Log Message:
Added support for maintaining oid-to-oid map in an external database; cleaned up tests and do-migration-tests to allow validation
--- /project/elephant/cvsroot/elephant/src/elephant/migrate.lisp 2007/03/11 03:31:09 1.11
+++ /project/elephant/cvsroot/elephant/src/elephant/migrate.lisp 2007/03/11 05:45:14 1.12
@@ -71,7 +71,6 @@
;;
-
(defgeneric migrate (dst src)
(:documentation
"Migrate an object from the src object, collection or controller
@@ -79,47 +78,9 @@
store so you can drop it into a parent object or the root of
the dst controller"))
-;; DEFAULT HANDLERS
-
-(defmethod migrate ((dst t) (src t))
- (error "Cannot migrate ~A of type ~A to destination of type ~A" src (type-of src) (type-of dst)))
-
-(defmethod migrate ((dst store-controller) (src t))
- "Default: standard objects are automatically migrated"
- src)
-
-;; Avoiding Duplication Semantics
-
-(defvar *migrate-copied-oids* (make-hash-table))
-(defvar *migrating* nil)
-
-;; ERROR CHECKING
-
-(defmethod migrate :around ((dst store-controller) (src store-controller))
- "This method ensures that we wipe our duplication detection
- around any top level call to migrate"
- (if *migrating*
- (call-next-method)
- (let ((*migrating* t))
- (declare (special *migrating*))
- (reset-migrate-duplicate-detection)
- (let ((result (call-next-method)))
- (reset-migrate-duplicate-detection)
- result))))
-
-(defmethod migrate :before ((dst store-controller) (src persistent))
- "This provides some sanity checking that we aren't trying to copy
- to the same controller. We also need to be careful about deadlocking
- our transactions among the two gets/puts. Each leaf migration should
- be in its own transaction to avoid too many write locks. "
- (let ((dst-spec (controller-spec dst)))
- (unless (object-was-copied-p src)
- (typecase src
- (store-controller (assert (not (equal dst-spec (controller-spec src)))))
- (persistent (assert (not (equal dst-spec (dbcn-spc-pst src)))))))))
-
;;
-;; WHOLE STORE MIGRATION
+;; MIGRATE ALL OBJECTS IN SRC STORE-CONTROLLER TO THE
+;; (TYPICALLY FRESH) DST STORE-CONTROLLER
;;
(defmethod migrate ((dst store-controller) (src store-controller))
@@ -184,33 +145,44 @@
old)))
;;
-;; Utilities for persistent objects
+;; HANDLE DEFAULTS
;;
-(defun reset-migrate-duplicate-detection ()
- "Reset oid map so that all references to a given object
- in the source only point to one copy in the target"
- (setf *migrate-copied-oids* (make-hash-table)))
+(defmethod migrate ((dst t) (src t))
+ (error "Cannot migrate ~A of type ~A to destination of type ~A" src (type-of src) (type-of dst)))
-(defun object-was-copied-p (src)
- "Test whether a source object has been copied"
- (and (subtypep (type-of src) 'persistent)
- (gethash (oid src) *migrate-copied-oids*)))
+(defmethod migrate ((dst store-controller) (src t))
+ "Default: standard objects are automatically migrated"
+ src)
-(defun register-copied-object (src dst)
- "When copying a source object, store it in the oid map"
- (assert (not (equal (dbcn-spc-pst src) (dbcn-spc-pst dst))))
- (setf (gethash (oid src) *migrate-copied-oids*) dst))
+;;
+;; ERROR CHECKING
+;;
-(defun retrieve-copied-object (src)
- "Get a copied object from the oid map"
- (gethash (oid src) *migrate-copied-oids*))
+(defmethod migrate :before ((dst store-controller) (src persistent))
+ "This provides some sanity checking that we aren't trying to copy
+ to the same controller. We also need to be careful about deadlocking
+ our transactions among the two gets/puts. Each leaf migration should
+ be in its own transaction to avoid too many write locks. "
+ (let ((dst-spec (controller-spec dst)))
+ (unless (object-was-copied-p src)
+ (typecase src
+ (store-controller (assert (not (equal dst-spec (controller-spec src)))))
+ (persistent (assert (not (equal dst-spec (dbcn-spc-pst src)))))))))
-(defmacro with-inhibited-slot-copy ((&key &allow-other-keys) &body body)
- "A user macro to support special slot handling in persistent objects"
- `(let ((*inhibit-slot-copy* t))
- (declare (special *inhibit-slot-copy*))
- ,@body))
+(defmethod migrate :before ((dst store-controller) (src store-controller))
+ "This method ensures that we reset duplicate object detection over the store-controller"
+ (initialize-migrate-duplicate-detection))
+
+(defmethod migrate :after ((dst store-controller) (src store-controller))
+ "This method ensures that we reset duplicate object detection over the store-controller"
+ (clear-migrate-duplicate-detection))
+
+(defmethod migrate ((dst store-controller) (src standard-class))
+ (error "Cannot migrate class objects (i.e. ~A)" src))
+
+(defmethod migrate ((dst store-controller) (src function))
+ (error "Cannot migrate function objects (i.e. ~A)" src))
;;
;; PERSISTENT OBJECTS
@@ -226,7 +198,7 @@
in the caller to keep the new object from having it's slots copied"
;; Copy or lookup persistent object
(if (object-was-copied-p src)
- (retrieve-copied-object src)
+ (retrieve-copied-object dst src)
(copy-persistent-object dst src)))
(defun copy-persistent-object (dstsc src)
@@ -259,13 +231,23 @@
(setf (slot-value-using-class class dst slot-def) value))))))
;;
+;; User utilities for persistent objects
+;;
+
+(defmacro with-inhibited-slot-copy ((&key &allow-other-keys) &body body)
+ "A user macro to support special slot handling in persistent objects"
+ `(let ((*inhibit-slot-copy* t))
+ (declare (special *inhibit-slot-copy*))
+ ,@body))
+
+;;
;; MIGRATE BTREE INDICES (override default persistent behavior)
;;
(defmethod migrate ((dst store-controller) (src btree))
"Copy an index and it's contents to the target repository"
(if (object-was-copied-p src)
- (retrieve-copied-object src)
+ (retrieve-copied-object dst src)
(let ((newbtree (build-btree dst)))
(ensure-transaction (:store-controller dst :txn-nosync t)
(copy-btree-contents dst newbtree src))
@@ -275,7 +257,7 @@
(defmethod migrate ((dst store-controller) (src indexed-btree))
"Also copy the inverse indices for indexed btrees"
(if (object-was-copied-p src)
- (retrieve-copied-object src)
+ (retrieve-copied-object dst src)
(let ((newbtree
(ensure-transaction (:store-controller dst :txn-nosync t)
(build-indexed-btree dst))))
@@ -298,8 +280,7 @@
src))
;;
-;; These functions handle standard objects that may contain nested indices or
-;; user-defined persistent objects.
+;; MIGRATE AGGREGATE LISP OBJECTS THAT MAY REFER TO OTHER PERSISTENT OBJECTS
;;
(defmethod migrate ((dst store-controller) (src standard-object))
@@ -309,10 +290,22 @@
as the serializer will, but copying any persistent objects found"
(let ((svs (slots-and-values src)))
(loop for i from 0 below (/ (length svs) 2) do
- (let ((name (pop svs))
+ (let ((slotname (pop svs))
(value (pop svs)))
- (setf (slot-value src name) (migrate dst value))))))
+ (setf (slot-value src slotname) (migrate dst value)))))
+ src)
+
+(defmethod migrate ((dst store-controller) (src structure-object))
+ "Walks structure slot values and ensures that any persistent references
+ are written back into the slot pointint to the new store"
+ (let ((svs (struct-slots-and-values src)))
+ (loop for i from 0 below (/ (length svs) 2) do
+ (let ((slotname (pop svs))
+ (value (pop svs)))
+ (setf (slot-value src slotname)
+ (migrate dst value)))))
+ src)
(defmethod migrate ((dst store-controller) (src cons))
"WARNING: This doesn't work for circular lists"
@@ -336,7 +329,72 @@
src)
src)
+;;
+;; MAINTAIN CORRESPONDENCE BETWEEN OLD STORE POBJS and NEW STORE POBJS
+;;
+
+(defvar *oid-hash* (make-hash-table))
+(defvar *oid-store* nil)
+(defvar *oid-spec* nil)
+(defvar *oid-btree* nil)
+
+(defun set-oid-spec (spec)
+ "Set to nil to perform oid mapping in memory, set to a valid spec to
+ perform the mapping on disk"
+ (setf *oid-spec* spec))
+
+(defun initialize-migrate-duplicate-detection ()
+ "Reset oid map so that all references to a given object
+ in the source only point to one copy in the target"
+ (if *oid-spec*
+ (progn
+ (setf *oid-store* (open-store *oid-spec* :recover t))
+ (setf *oid-btree* (make-btree *oid-store*))
+ (setf *oid-hash* nil))
+ (progn
+ (setf *oid-hash* (make-hash-table))
+ (setf *oid-btree* nil))))
+
+(defun clear-migrate-duplicate-detection ()
+ (when *oid-spec*
+ (setf *oid-btree* nil)
+ (close-store *oid-store*)
+ (setf *oid-store* nil))
+ (when *oid-hash*
+ (setf *oid-hash* nil)))
+
+(defun object-was-copied-p (src)
+ "Test whether a source object has been copied"
+ (assert (subtypep (type-of src) 'persistent))
+ (cond (*oid-btree*
+ (existsp (oid src) *oid-btree*))
+ (*oid-hash*
+ (gethash (oid src) *oid-hash*))
+ (t (warn "Test for persistent copy not inside top level call; returning nil")
+ nil)))
+
+
+(defun register-copied-object (src dst)
+ "When copying a source object, store it in the oid map"
+ (assert (not (equal (dbcn-spc-pst src) (dbcn-spc-pst dst))))
+ (when (or *oid-btree* *oid-hash*)
+ (if *oid-btree*
+ (setf (get-value (oid src) *oid-btree*)
+ (cons (oid dst) (type-of dst)))
+ (setf (gethash (oid src) *oid-hash*) dst))))
+(defun retrieve-copied-object (dst src)
+ "Get a copied object from the oid map"
+ (assert (subtypep (type-of dst) 'store-controller))
+ (cond (*oid-btree*
+ (let ((record (get-value (oid src) *oid-btree*)))
+ (get-cached-instance dst (car record) (cdr record))))
+ (*oid-hash*
+ (gethash (oid src) *oid-hash*))
+ (t (error "Cannot retrieve an object from oid-to-oid map
+ when not inside top-level call"))))
+
+
--- /project/elephant/cvsroot/elephant/src/elephant/package.lisp 2007/03/06 04:15:27 1.22
+++ /project/elephant/cvsroot/elephant/src/elephant/package.lisp 2007/03/11 05:45:14 1.23
@@ -64,7 +64,7 @@
#:struct-constructor
- #:migrate #:*inhibit-slot-copy*
+ #:migrate #:set-oid-spec #:*inhibit-slot-copy*
#:add-symbol-conversion #:add-package-conversion
#:*always-convert*