Carl,
If you have a chance to look at this, be my guest. Anyone is free to
comment, of course.
> (1) Read-back-in-time isolation between clients. Every
> transaction sees all objects in the state they were in
> when the transaction started.
I've been looking at this in more detail, not particularly from the point of
view of iterators, but as a general approach to concurrent database sessions
belonging to different clients.
Each client is connected to the database via a "session" which is maintained
by the server.
The session is itself an object in the database, having a path from the
root. The session maintains as a field a set of all of the objects the
client has been told about (in this session). This ensures that the
concurrent garbage collector doesn't throw away objects which the client has
created, but hasn't yet stored in fields of other objects in the database
reachable from the root: all objects known to all logged-on client are safe
from GC.
A client (person, local process, remote batch program, etc) may interrogate
the database in two ways: (1) "browsing", during which other clients may be
modifying the database concurrently, and so successive enquiries may see
changing values, and (2) "transacting", which is the only time mutating
requests may be made, and during which the database will appear to the
transacting client to be exclusive. A client is browsing until a transation
is started, then is transacting until the transaction is either committed or
abandoned, then is browsing again, etc.
All changes made during a transaction are visible only to the client
performing the transaction. When a transaction is committed, all changes
requested by the client are made in a single, failproof, atomic action by
the server.
Things get interesting when the following sequence of events takes place:
(1) Client A begins a transaction
(2) Client A requests some info about object X
(3) Client B begins a transaction
(4) Client B requests that object X be mutated
(5) Client B commits
(6) Client A requests some more information about object X
(7) Client A requests that object X be mutated
(

Client A commits
Client A must see the same X in steps (2) and (6), despite the fact the B
has mutated it. When client A commits, B's changes to X will be wiped out,
but that's what we'd expect.
OK, so that's the theory. How is it implemented in practice? Forgive me,
but I haven't studied ODBMS. What I now present (if correct!) is probably
well-known technology, but I've always enjoyed algorithmics, and so it's fun
to try to find my own solutions.
A client knows an object by a handle. The same handle always refers to the
same object (during a session, anyway -- I haven't yet decided whether
handles can change in the long term).
During a transaction, the server session object for that client maintains a
"substition map" (SM), initially empty. Whenever the client make a mutating
request, the server makes a copy of the object to be mutated (if it hasn't
already), and mutates the copy. It then stores an entry in the SM whose key
is the handle of the original object, and whose value is handle of the new
object.
All handles referred to by the client in requests during a transaction are
"translated" via the SM if necessary, so that the requests are actually
carried out on the copies.
If the transaction is abandoned, the copies are simply thrown away, and the
SM cleared. (The copies will probably hang around until the GC discards
them, although I might try and improve that later -- when I write the GC!)
If the transaction is committed, the server atomically *exchanges the
identities of each of the key-value pairs in the SM*, and then clears the
SM. That is to say, for an entry
handle-of-X : handle-of-X-copy
the server makes the first handle point to the mutated copy and the second
to the original. (This is a mutual become: in Smalltalk language.)
But that isn't quite the end of the story.
If some other client A is mid-transaction when a transaction by client B is
committed, client A may perceive a change in the database, which isn't
allowed. In particular, any object which A *hasn't* yet mutated will appear
to have changed when B's transaction exchanges identities.
So before B exchanges each pair of identities in its SM, it must show its SM
to all other sessions with transactions in progress. Each of those session
scans B's SM, and adds any entry whose key is found in its
"user-knows-about-this-object" set but not among the keys of its own SM to a
secondary substitution map.
Ie, if B's SM contains
handle-of-X : handle-of-X-copy
A adds
handle-of-X : handle-of-X-copy
to its secondary substition map. Then when B exchanges identities of X and
X-copy, A's entry will effectively say:
handle-of-X-copy : handle-of-X
which will ensure that if client A uses the only handle to X it knows about
(the first), the server will ensure that the *original* is still referred
to.
I need to work out some details yet about what happens to entries in the
secondary list when A attempts to mutate them, and also what happens if yet
another session attempts to modify X: some some of transitive operations are
going on here.
Does all this seem familiar? Does it look right?
Cheers, Paul
>> Stay informed about: Idempotent ODBMS iterators