Oh yeah, there is something interesting going on in the wrapper, We keep track of the ongoing transactions by mapping the
user-id
to a list of transaction contexts (every nested transaction by a user "pushes" a new txn-context
). Anyway, it's the
repository-persistent-information
that has the interesting stuff:(defclass repository-persistent-information () ( (type :initarg :type :initform (error "Required initarg :type omitted.") :reader repository-persistent-information/type :type repository-type) ;; Database parent is the root extent for an extent database, or the master database for a satellite. ;; Root extents or master repositories won't have a parent (parent-repository :initarg :parent-repository :initform nil :reader repository-persistent-information/parent :type (optional relative-pathname)) ;; Satellite repositories is non-nil only for master repositories. (satellite-repositories :initform nil :initarg :satellite-repositories :accessor repository-persistent-information/satellite-repositories) (canonical-class-dictionary :initform (make-instance 'canonical-class-dictionary) :reader repository-persistent-information/canonical-class-dictionary) (cid-master-table :initform (make-instance 'cid-master-table) :reader repository-persistent-information/cid-master-table) (root-mapper :initarg :root-mapper :initform (error "Required initarg :root-mapper omitted.") :reader repository-persistent-information/root-mapper) (cid-mapper :initarg :cid-mapper :initform (error "Required initarg :cid-mapper omitted.") :reader repository-persistent-information/cid-mapper) (local-mapper :initarg :local-mapper :initform (error "Required initarg :local-mapper omitted.") :reader repository-persistent-information/local-mapper) (locally-named-roots :initarg :locally-named-roots :initform (error "Required initarg :locally-named-roots omitted.") :reader repository-persistent-information/locally-named-roots) (anonymous-user :initarg :anonymous-user :initform nil :reader repository-persistent-information/anonymous-user)) (:default-initargs :node-id +object-id-of-root+) ;; force this to always be the root object. (:documentation "Persistent information describing a repositiory, and stored in the repository") (:metaclass persistent-standard-class) (:schema-version 0))
The
repository-type
is just a keyword:(defconstant *repository-types* '(:basic :master :satellite :transport :extent :workspace) "Type of repositories. Note that all but :EXTENT types of repositories serve as root extents for databases which have multiple extents, and therefore imply extent.")The
parent-repository
and thesatellite-repositories
are for juggling multiple "satellite" repositories for holding particular subsets of changes (for, say, geographically distributing the servers for different product groups).The
canonical-class-dictionary
is an intern table for objects.The
cid-master-table
is (logically) the collection of audit-records. A CID (after change id) is represented as an integer index into the master table.The
root-mapper
is a mapping table from distributed identifiers to objects. The
cid-mapper
is a mapping table from the distributed identifier that represents the CID to the integer index of that CID in the master table. It is a subtable of the local mapper.The
local-mapper
is submapping of the root-mapping
, but a supermapping of the cid-mapper
.The
locally-named-roots
is a hash table for storing the root objects of the repository.Finally, there is the
anonymous-user
slot, which is the user id assigned for bootstrapping.And all this crap is in support of this procedure:
(defun call-with-repository-transaction (&key repository transaction-type user-id-specifier reason ;; generally, you only want to specify these two meta-cid-set-specifier cid-set-specifier ;; but if you are doing a comparison, ;; specify these as well aux-meta-cid-set-specifier aux-cid-set-specifier receiver) (check-type user-id-specifier (or keyword distributed-identifier)) (check-type transaction-type repository-transaction-type) (check-type reason string) ;; implementation omitted for brevity, ha ha )
Naturally we need to specify the
:repository
, the :transaction-type
is one of (defconstant *repository-transaction-types* '(:read-only :read-write :read-cons :read-only-compare :read-cons-nonversioned :read-only-nonversioned :read-write-nonversioned))The
:user-id-specifier
should be a distributed-identifier
of a core-user
instance.The
:reason
is a human readable string describing the transaction. The
:meta-cid-set-specifier
is mumble, mumble... just a sec...The
:cid-set-specifier
is how you specify which CIDs will form the basis view for the transaction. We allow this to be a procedure that returns a cid-set
object, and we will call this procedure as we are setting up the transaction and use the :meta-cid-set-specifier
to specify the CIDs to form the versioned view the procedure will see. The
:meta-cid-set-specifier
can be the symbol :latest-metaversion
, a timestamp, or a cid-set
. :latest-metaversion
means to use all CIDS while resolving the :cid-set-specifier
, a timestamp is useful for rewinding the world, and the main use for using an explicit cid-set is for synchronizing views between master and satellite repositories.The
:receiver
is invoked within the dynamic extent of a transaction. It is passed a core-txn
object that contains the metadata associated with the transaction.The ChangeSafe core components are the repository that holds changes and associated meta-information, and simple versioned CLOS objects. It is only useful as a foundation layer, though.
Next up, another level of abstraction...