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. -@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.
-@subsection Synchronizing Code and Database +For transient slots the behavior is the same as it is in CLOS for +all in-memory slots.
+@itemize +@item Added slots: are added to the object and their initforms + called just as if they were created without initargs +@item Discarded slots: are dropped and their values lost +@end itemize + +Persistent slots have a slightly different behavior, as only resident +(those with valid placeholder objects) objects are updated. + +@itemize +@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} +@item Added slots (non-resident): the added slots will have unbound values +@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. +@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 +@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. + +@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)} + +@subsection Support for @code{change-class} + +Elephant also supports the @code{change-class} by overloading +@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.
-@c @node Querying persistent instances -@c @comment node-name, next, previous, up -@c @section Querying persistent instances -@c -@c A SQL select-like interface is in the works, but for now queries are -@c limited to manual mapping over class instances or doing small queries -@c with @code{get-instances-*} functions. One advantage of this is that -@c it is easy to estimate the performance costs of your queries and to -@c choose standard and derived indices that give you the ordering and -@c performance you want. -@c -@c There is, however, a quick and dirty query API example that is not -@c officially supported in the release but is intended to invite comment. -@c This is an example of a full query system that would automatically -@c perform joins, use the appropriate indices and perhaps even adaptively -@c suggest or add indices to facilitate better performance on common -@c queries. -@c -@c There are two functions @ref{Function elephant:get-query-instances} -@c and @ref{Function elephant:map-class-query} which accept a set of -@c constraints instead of the familiar value or range arguments. -@c -@c We'll use the classes @code{person} and @code{department} to -@c illustrate how to perform queries over a set of objects that may be -@c constrainted by their relationships to other objects. -@c -@c @lisp -@c (defpclass person () -@c ((name :initarg :name :index t) -@c (salary :initarg :salary :index t) -@c (department :initarg :dept))) -@c -@c (defmethod print-object ((p person) stream) -@c (format stream "#<PERS: ~A>" (slot-value p 'name))) -@c -@c (defun print-name (inst) -@c (format t "Name: ~A~%" (slot-value inst 'name))) -@c -@c (defpclass department () -@c ((name :initarg :name) -@c (manager :initarg :manager))) -@c -@c (defmethod print-object ((d department) stream) -@c (format stream "#<DEPT ~A, mgr = ~A>" -@c (slot-value d 'name) -@c (when (slot-boundp d 'manager) -@c (slot-value (slot-value d 'manager) 'name)))) -@c @end lisp -@c -@c Here we have a simple employee database with managers (also of type -@c person) and departments. This simple system will provide fodder for -@c some reasonably complex constraints. Let's create a few departments. -@c -@c @lisp -@c (setf marketing (make-instance 'department :name "Marketing")) -@c (setf engineering (make-instance 'department :name "Engineering")) -@c (setf sales (make-instance 'department :name "Sales")) -@c @end lisp -@c -@c And manager @code{people} for the departments. -@c -@c @lisp -@c (make-instance 'person :name "George" :salary 140000 :department marketing) -@c (setf (slot-value marketing 'manager) *) -@c -@c (make-instance 'person :name "Sally" :salary 140000 :department engineering) -@c (setf (slot-value engineering 'manager) *) -@c -@c (make-instance 'person :name "Freddy" :salary 180000 :department sales) -@c (setf (slot-value sales 'manager) *) -@c @end lisp -@c -@c And of course we need some folks to manage -@c -@c @lisp -@c (defparameter *names* -@c '("Jacob" "Emily" "Michael" "Joshua" "Andrew" "Olivia" "Hannah" "Christopher")) -@c -@c (defun random-element (list) -@c "Choose a random element from the list and return it" -@c (nth (random (length list)) list)) -@c -@c (with-transaction () -@c (loop for i from 0 upto 40 do -@c (make-instance 'person -@c :name (format nil "~A~A" (random-elephant *names*) i) -@c :salary (floor (+ (* (random 1000) 100) 30000)) -@c :department (case (random 3) -@c (0 marketing) -@c (1 engineering) -@c (2 sales))))) -@c @end lisp -@c -@c -@c Now we can look at a few queries. -@c -@c @lisp -@c (defun get-managers () -@c (get-query-instances `((person -@c -@c For those familiar with SQL, if an instance of @code{person} has a -@c pointer to an instance of @code{department} then that relation can be -@c used to perform a join. Of course joins in the object world won't -@c return a table, instead they will return conjunctions of objects that -@c satisfy a mutual set of constraints. +@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 +@code{*default-indexed-class-synch-policy*} with the appropriate +policy: + +@itemize +@item :class - The class is the master, and indices are deleted for any slots + that are no longer indexed +@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. +@item :union - This does what you would expect, updates the class to match any + existing indices and creates new indices. +@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. + +@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
-@node BTree Indicies +@node The BTree Index @comment node-name, next, previous, up -@section BTree Indicies +@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. + +@c @node Querying persistent instances +@c @comment node-name, next, previous, up +@c @section Querying persistent instances +@c +@c A SQL select-like interface is in the works, but for now queries are +@c limited to manual mapping over class instances or doing small queries +@c with @code{get-instances-*} functions. One advantage of this is that +@c it is easy to estimate the performance costs of your queries and to +@c choose standard and derived indices that give you the ordering and +@c performance you want. +@c +@c There is, however, a quick and dirty query API example that is not +@c officially supported in the release but is intended to invite comment. +@c This is an example of a full query system that would automatically +@c perform joins, use the appropriate indices and perhaps even adaptively +@c suggest or add indices to facilitate better performance on common +@c queries. +@c +@c There are two functions @ref{Function elephant:get-query-instances} +@c and @ref{Function elephant:map-class-query} which accept a set of +@c constraints instead of the familiar value or range arguments. +@c +@c We'll use the classes @code{person} and @code{department} to +@c illustrate how to perform queries over a set of objects that may be +@c constrainted by their relationships to other objects. +@c +@c @lisp +@c (defpclass person () +@c ((name :initarg :name :index t) +@c (salary :initarg :salary :index t) +@c (department :initarg :dept))) +@c +@c (defmethod print-object ((p person) stream) +@c (format stream "#<PERS: ~A>" (slot-value p 'name))) +@c +@c (defun print-name (inst) +@c (format t "Name: ~A~%" (slot-value inst 'name))) +@c +@c (defpclass department () +@c ((name :initarg :name) +@c (manager :initarg :manager))) +@c +@c (defmethod print-object ((d department) stream) +@c (format stream "#<DEPT ~A, mgr = ~A>" +@c (slot-value d 'name) +@c (when (slot-boundp d 'manager) +@c (slot-value (slot-value d 'manager) 'name)))) +@c @end lisp +@c +@c Here we have a simple employee database with managers (also of type +@c person) and departments. This simple system will provide fodder for +@c some reasonably complex constraints. Let's create a few departments. +@c +@c @lisp +@c (setf marketing (make-instance 'department :name "Marketing")) +@c (setf engineering (make-instance 'department :name "Engineering")) +@c (setf sales (make-instance 'department :name "Sales")) +@c @end lisp +@c +@c And manager @code{people} for the departments. +@c +@c @lisp +@c (make-instance 'person :name "George" :salary 140000 :department marketing) +@c (setf (slot-value marketing 'manager) *) +@c +@c (make-instance 'person :name "Sally" :salary 140000 :department engineering) +@c (setf (slot-value engineering 'manager) *) +@c +@c (make-instance 'person :name "Freddy" :salary 180000 :department sales) +@c (setf (slot-value sales 'manager) *) +@c @end lisp +@c +@c And of course we need some folks to manage +@c +@c @lisp +@c (defparameter *names* +@c '("Jacob" "Emily" "Michael" "Joshua" "Andrew" "Olivia" "Hannah" "Christopher")) +@c +@c (defun random-element (list) +@c "Choose a random element from the list and return it" +@c (nth (random (length list)) list)) +@c +@c (with-transaction () +@c (loop for i from 0 upto 40 do +@c (make-instance 'person +@c :name (format nil "~A~A" (random-elephant *names*) i) +@c :salary (floor (+ (* (random 1000) 100) 30000)) +@c :department (case (random 3) +@c (0 marketing) +@c (1 engineering) +@c (2 sales))))) +@c @end lisp +@c +@c +@c Now we can look at a few queries. +@c +@c @lisp +@c (defun get-managers () +@c (get-query-instances `((person +@c +@c For those familiar with SQL, if an instance of @code{person} has a +@c pointer to an instance of @code{department} then that relation can be +@c used to perform a join. Of course joins in the object world won't +@c return a table, instead they will return conjunctions of objects that +@c satisfy a mutual set of constraints. +