[elephant-cvs] CVS elephant/doc

ieslick ieslick at common-lisp.net
Tue Apr 24 16:39:30 UTC 2007


Update of /project/elephant/cvsroot/elephant/doc
In directory clnet:/tmp/cvs-serv14475/doc

Modified Files:
	user-guide.texinfo 
Log Message:
Handle error conditions in change-class protocol; more docs

--- /project/elephant/cvsroot/elephant/doc/user-guide.texinfo	2007/04/24 12:58:10	1.15
+++ /project/elephant/cvsroot/elephant/doc/user-guide.texinfo	2007/04/24 16:39:30	1.16
@@ -10,10 +10,9 @@
 * Serialization details:: The devil hides in the details.
 * Persistent Classes and Objects:: All the dirt on persistent objects.
 * Class Indices:: In-depth discussion about indexing persistent indices.
- at c * Querying persistent instances:: Retrieving instances of classes.
 * Using BTrees:: Using the native btree.
 * Using Cursors:: Low-level access to BTrees.
-* BTree Indices:: Alternative ways to index collections.
+* The BTree index:: Alternative ways to reference objects in btrees
 * Transaction Details:: Develop a deeper understanding of transactions and avoid the pitfalls.
 * Multi-repository Operation:: Specifying repositories.
 * Multi-threaded Applications:: What considerations are required for safe multi-threading
@@ -643,21 +642,70 @@
 
 @subsection Class Redefinition
 
-This section discusses the appropriate model a user should employ in
-thinking about the behavior of persistent object when their clases are
-redefined.
-
-- What happens when you redefine a class online? 
-- Drop & add slots?  Change slot status?
-- What if you connect to an old database with a new class specification?
-  (ref to class indicies behavior)
-
-MOP:
-- update-instance-for-redefinied-class
-- update-instance-for-different-class
+Class redefinition is problematic in the current (0.9) version of
+Elephant.  The usual CLOS mechanisms are properly implemented, but
+updating instances will only work for those instances that are in
+memory at the time.  Instances that are non-resident will not be
+updated.  This is usually not as big a problem as it seems, because
+the slot values are stored independently.  An outline of the update
+procedure follows:
+
+The function @code{update-instance-for-redefined-class} is called by
+CLOS whenever @code{defclass} is re-evaluated and results in a change
+in the list of slots.  
 
- at subsection Synchronizing Code and Database
+For transient slots the behavior is the same as it is in CLOS for
+all in-memory slots.
 
+ at itemize
+ at item Added slots: are added to the object and their initforms
+      called just as if they were created without initargs
+ at item Discarded slots: are dropped and their values lost
+ at end itemize
+
+Persistent slots have a slightly different behavior, as only resident
+(those with valid placeholder objects) objects are updated.
+
+ at itemize
+ at item Added slots (resident): are added to the object and the initforms
+      are called only on in-memory objects, as in an empty call to
+      @code{make-instance}
+ at item Added slots (non-resident): the added slots will have unbound values
+ at item Discarded slots (resident): slots are dropped from the class and become 
+      inaccessible, but their values are not deleted from the database.  This
+      is a precautionary measure as losing persistent data because of an 
+      accidental re-evaluation while editing a defclass could be painful.  If
+      you add the slot back, the original value will be accessible regardless of
+      the initform.
+ at item Discarded slots (non-resident): This has the same behavior as resident objects, 
+      as no side effects are made on the objects or their slots
+ at end itemize
+
+There are additional considerations for matching class indexing
+options in the class object to the actual indices in the database.
+The following section will discuss synchronizing these if they
+diverge.
+
+ at emph{(Note: release 0.9.1 should fix this by providing an oid->class map that allows
+the system to cheaply iterate over all objects and update them appropriately.  This 
+hasn't been done yet due to performance implications.  See Trac system for the appropriate
+tickets)}
+
+ at subsection Support for @code{change-class}
+
+Elephant also supports the @code{change-class} by overloading
+ at code{update-instance-for-different-class}.  The handling of slots in
+this case is identical to the class redefinition above.  Persistent
+and transient slot values are retained if their name matches a
+slotname in the new class and initforms are called on newly added
+slots.  Valid initargs for any slot will override this default behavior
+and set the slot value to the initarg value.
+
+Because the instance is guaranteed to be resident, the operation has none of the
+resident/non-resident conflicts above.
+
+Changing a persistent instance to a non-persistent class is not
+allowed and will result in an error.
 
 @node Class Indices
 @comment node-name, next, previous, up
@@ -706,111 +754,43 @@
 somewhat user customizable; documentation for this exists in the source
 file referenced above.
 
- at c @node Querying persistent instances
- at c @comment node-name, next, previous, up
- at c @section Querying persistent instances
- at c 
- at c A SQL select-like interface is in the works, but for now queries are
- at c limited to manual mapping over class instances or doing small queries
- at c with @code{get-instances-*} functions.  One advantage of this is that
- at c it is easy to estimate the performance costs of your queries and to
- at c choose standard and derived indices that give you the ordering and
- at c performance you want.
- at c 
- at c There is, however, a quick and dirty query API example that is not
- at c officially supported in the release but is intended to invite comment.
- at c This is an example of a full query system that would automatically
- at c perform joins, use the appropriate indices and perhaps even adaptively
- at c suggest or add indices to facilitate better performance on common
- at c queries.
- at c 
- at c There are two functions @ref{Function elephant:get-query-instances}
- at c and @ref{Function elephant:map-class-query} which accept a set of
- at c constraints instead of the familiar value or range arguments.
- at c 
- at c We'll use the classes @code{person} and @code{department} to
- at c illustrate how to perform queries over a set of objects that may be
- at c constrainted by their relationships to other objects.
- at c 
- at c @lisp
- at c (defpclass person ()
- at c   ((name :initarg :name :index t)
- at c    (salary :initarg :salary :index t)
- at c    (department :initarg :dept)))
- at c 
- at c (defmethod print-object ((p person) stream)
- at c   (format stream "#<PERS: ~A>" (slot-value p 'name)))
- at c 
- at c (defun print-name (inst)
- at c   (format t "Name: ~A~%" (slot-value inst 'name)))
- at c 
- at c (defpclass department ()
- at c   ((name :initarg :name)
- at c    (manager :initarg :manager)))
- at c 
- at c (defmethod print-object ((d department) stream)
- at c   (format stream "#<DEPT ~A, mgr = ~A>"
- at c           (slot-value d 'name)
- at c           (when (slot-boundp d 'manager)
- at c                 (slot-value (slot-value d 'manager) 'name))))
- at c @end lisp
- at c 
- at c Here we have a simple employee database with managers (also of type
- at c person) and departments.  This simple system will provide fodder for
- at c some reasonably complex constraints.  Let's create a few departments.
- at c 
- at c @lisp
- at c (setf marketing (make-instance 'department :name "Marketing"))
- at c (setf engineering (make-instance 'department :name "Engineering"))
- at c (setf sales (make-instance 'department :name "Sales"))
- at c @end lisp
- at c 
- at c And manager @code{people} for the departments.
- at c 
- at c @lisp
- at c (make-instance 'person :name "George" :salary 140000 :department marketing)
- at c (setf (slot-value marketing 'manager) *)
- at c 
- at c (make-instance 'person :name "Sally" :salary 140000 :department engineering)
- at c (setf (slot-value engineering 'manager) *)
- at c 
- at c (make-instance 'person :name "Freddy" :salary 180000 :department sales)
- at c (setf (slot-value sales 'manager) *)
- at c @end lisp
- at c 
- at c And of course we need some folks to manage
- at c 
- at c @lisp
- at c (defparameter *names*
- at c   '("Jacob" "Emily" "Michael" "Joshua" "Andrew" "Olivia" "Hannah" "Christopher"))
- at c 
- at c (defun random-element (list)
- at c   "Choose a random element from the list and return it"
- at c   (nth (random (length list)) list))
- at c 
- at c (with-transaction ()
- at c   (loop for i from 0 upto 40 do
- at c     (make-instance 'person
- at c       :name (format nil "~A~A" (random-elephant *names*) i)
- at c       :salary (floor (+ (* (random 1000) 100) 30000))
- at c       :department (case (random 3)
- at c                     (0 marketing)
- at c                     (1 engineering)
- at c                     (2 sales)))))
- at c @end lisp
- at c 
- at c 
- at c Now we can look at a few queries.
- at c 
- at c @lisp
- at c (defun get-managers ()
- at c   (get-query-instances `((person
- at c 
- at c For those familiar with SQL, if an instance of @code{person} has a
- at c pointer to an instance of @code{department} then that relation can be
- at c used to perform a join.  Of course joins in the object world won't
- at c return a table, instead they will return conjunctions of objects that
- at c satisfy a mutual set of constraints.
+ at subsection Synchronizing Classes and Data Stores
+
+Sometimes you may change a defclass form and then connect to a
+database with instances that do not match the current defclass
+definition.  Because of the defclass behavior above, there is no need
+to detect this case as the behavior will be as if all instances were
+non-resident at redefinition time.  However, this is an issue for
+indexed classes as the cost of indexing is high.  There is a
+synchronization policy which updates either the class or the online
+class indexing mechanism at the time you try to perform an index
+operation (i.e. when @code{find-class-index} is called).
+
+A policy is selected by setting the value of
+ at code{*default-indexed-class-synch-policy*} with the appropriate
+policy:
+
+ at itemize
+ at item :class - The class is the master, and indices are deleted for any slots
+               that are no longer indexed
+ at item :db - The database is the master and the class indexing annotations are
+            updated so that the slots that satisfy @code{class-indexedp-by-name}
+            are isomorphic to the existing indices in the db.
+ at item :union - This does what you would expect, updates the class to match any
+               existing indices and creates new indices.
+ at end itemize
+
+Derived slots can be problematic as they may depend on slot values
+that no longer exist in the changed defclass.  This will result in an
+error, so for now you will have to manage any mismatches such as this
+yourself.
+
+ at emph{Note: release 0.9.1 should fix both mismatches and performance issues related
+to derived indices by allowing the user to provide hints as to which slot values the
+index depends.  This will allow the system to only update when the appropriate slots
+change and to delete or inhibit derived indicies when slots are deleted.  We will also
+improve error handling for this case, so you can delete the derived index and continue
+performing the write to a persistent object that flagged the error.}
 
 @node Using BTrees
 @comment node-name, next, previous, up
@@ -829,15 +809,17 @@
 blocks of data is relatively inexpensive after a seek and comparisons
 on objects that are stored in memory is cheap.
 
+
+
 @node Using Cursors
 @comment node-name, next, previous, up
 @section Using Cursors
 
-Empty.
+- initialized, not-initialized
 
- at node BTree Indicies
+ at node The BTree Index
 @comment node-name, next, previous, up
- at section BTree Indicies
+ at section The BTree Index
 
 Empty.
 
@@ -1103,10 +1085,123 @@
 @section CL-SQL Data Store
 
 
+
 @node Postmodern Data Store
 @comment node-name, next, previous, up
 @section Postmodern Data Store
 
+The postmodern data store is not yet integrated.  It should be documented 
+for the forthcoming release 0.9.1 or 0.9.2.
+
 @node Native Lisp Data Store
 @comment node-name, next, previous, up
 @section Native Lisp Data Store
+
+The native lisp data store is vaporware at this time.
+
+ at c @node Querying persistent instances
+ at c @comment node-name, next, previous, up
+ at c @section Querying persistent instances
+ at c 
+ at c A SQL select-like interface is in the works, but for now queries are
+ at c limited to manual mapping over class instances or doing small queries
+ at c with @code{get-instances-*} functions.  One advantage of this is that
+ at c it is easy to estimate the performance costs of your queries and to
+ at c choose standard and derived indices that give you the ordering and
+ at c performance you want.
+ at c 
+ at c There is, however, a quick and dirty query API example that is not
+ at c officially supported in the release but is intended to invite comment.
+ at c This is an example of a full query system that would automatically
+ at c perform joins, use the appropriate indices and perhaps even adaptively
+ at c suggest or add indices to facilitate better performance on common
+ at c queries.
+ at c 
+ at c There are two functions @ref{Function elephant:get-query-instances}
+ at c and @ref{Function elephant:map-class-query} which accept a set of
+ at c constraints instead of the familiar value or range arguments.
+ at c 
+ at c We'll use the classes @code{person} and @code{department} to
+ at c illustrate how to perform queries over a set of objects that may be
+ at c constrainted by their relationships to other objects.
+ at c 
+ at c @lisp
+ at c (defpclass person ()
+ at c   ((name :initarg :name :index t)
+ at c    (salary :initarg :salary :index t)
+ at c    (department :initarg :dept)))
+ at c 
+ at c (defmethod print-object ((p person) stream)
+ at c   (format stream "#<PERS: ~A>" (slot-value p 'name)))
+ at c 
+ at c (defun print-name (inst)
+ at c   (format t "Name: ~A~%" (slot-value inst 'name)))
+ at c 
+ at c (defpclass department ()
+ at c   ((name :initarg :name)
+ at c    (manager :initarg :manager)))
+ at c 
+ at c (defmethod print-object ((d department) stream)
+ at c   (format stream "#<DEPT ~A, mgr = ~A>"
+ at c           (slot-value d 'name)
+ at c           (when (slot-boundp d 'manager)
+ at c                 (slot-value (slot-value d 'manager) 'name))))
+ at c @end lisp
+ at c 
+ at c Here we have a simple employee database with managers (also of type
+ at c person) and departments.  This simple system will provide fodder for
+ at c some reasonably complex constraints.  Let's create a few departments.
+ at c 
+ at c @lisp
+ at c (setf marketing (make-instance 'department :name "Marketing"))
+ at c (setf engineering (make-instance 'department :name "Engineering"))
+ at c (setf sales (make-instance 'department :name "Sales"))
+ at c @end lisp
+ at c 
+ at c And manager @code{people} for the departments.
+ at c 
+ at c @lisp
+ at c (make-instance 'person :name "George" :salary 140000 :department marketing)
+ at c (setf (slot-value marketing 'manager) *)
+ at c 
+ at c (make-instance 'person :name "Sally" :salary 140000 :department engineering)
+ at c (setf (slot-value engineering 'manager) *)
+ at c 
+ at c (make-instance 'person :name "Freddy" :salary 180000 :department sales)
+ at c (setf (slot-value sales 'manager) *)
+ at c @end lisp
+ at c 
+ at c And of course we need some folks to manage
+ at c 
+ at c @lisp
+ at c (defparameter *names*
+ at c   '("Jacob" "Emily" "Michael" "Joshua" "Andrew" "Olivia" "Hannah" "Christopher"))
+ at c 
+ at c (defun random-element (list)
+ at c   "Choose a random element from the list and return it"
+ at c   (nth (random (length list)) list))
+ at c 
+ at c (with-transaction ()
+ at c   (loop for i from 0 upto 40 do
+ at c     (make-instance 'person
+ at c       :name (format nil "~A~A" (random-elephant *names*) i)
+ at c       :salary (floor (+ (* (random 1000) 100) 30000))
+ at c       :department (case (random 3)
+ at c                     (0 marketing)
+ at c                     (1 engineering)
+ at c                     (2 sales)))))
+ at c @end lisp
+ at c 
+ at c 
+ at c Now we can look at a few queries.
+ at c 
+ at c @lisp
+ at c (defun get-managers ()
+ at c   (get-query-instances `((person
+ at c 
+ at c For those familiar with SQL, if an instance of @code{person} has a
+ at c pointer to an instance of @code{department} then that relation can be
+ at c used to perform a join.  Of course joins in the object world won't
+ at c return a table, instead they will return conjunctions of objects that
+ at c satisfy a mutual set of constraints.
+




More information about the Elephant-cvs mailing list