[rucksack-devel] Re: Rucksack, ECLM
Nikodemus Siivola
nikodemus at random-state.net
Wed May 17 15:54:09 UTC 2006
Getting back to this after pulling my head out of my rectum...
"Arthur Lemmens" <alemmens at xs4all.nl> writes:
> Now that I think about it, maybe I don't understand you correctly when
> you say that the younger transaction should be aborted. I thought that
> you meant that it should be aborted at step 2 in your scenario, but
> maybe you're saying that it should only be aborted at step 3? *That*
> sounds like it could be a reasonable solution. It makes use of multiple
> versions as long as possible and comes up with a simple non-locking
> conflict resolution when there really is no other choice left.
> I think I'll go for that, unless you, Luke or Edi has a good reason why
> I shouldn't. (Luke, do you think this also solves your left-to-right
> vs. right-to-left scenario? I think that was basically the same thing
> that Nikodemus describes here, but maybe I'm missing something?
I said earlier that this was what I ment, but that is wrong.
Scenario:
C (counter) has value 0.
A (transaction) enters.
A increments C -> 1.
B (transaction) enters.
B sees the pristine C (as A hasn't committed yet), and increments it C -> 1.
at this point only one of the two can commit, as their views aren't
consistent anymore. After both have succesfully committed C should b
2.
Since rollbacks are expensive the abort should happen as soon as
possible, and it needs to be B so that younger transactions cannot
starve older ones. Ergo, B should be aborted when it tries to
increment C.
So in this scenario copy-on-write doesn't buy much. :/
(If this was what _you_ ment, then everything is dandy and I was
just being blind.)
Other stuff:
* Rucksacks: There are currently provisions for multiple Rucksacks,
which I find good, but a single object can only belong to a single
Rucksack (which I find good too).
I am, however, a bit confused on what is the intention in the
following cases:
(defclass x ()
((y :persistence t :accessor y-of))
(:metaclass persistent-class))
(let ((x (make-instance 'x)))
;; OK so far -- X may is potentially persitent, but not reachable
;; from any root-set or index, so no problems.
(with-rucksack (r *my-rucksack*)
(with-transaction ()
(add-rucksack-root x r)))
;; Still OK -- X has been saved, but that's all there is to it...
;; Untill we try the following:
(setf (y-of x) 'y)
;; If this works, then I believe the instance in *my-rucksack* should be
;; updated -- otherwise persistence is a very precarious thing!
;;
;; If this fails, then what we have is a "dangling object"... not nice.
;;
;; In either case, another hairy case follows:
(with-rucksack (r *other-rucksack*)
(with-transaction ()
(add-rucksack-root x r)))
;; ...either this should signal an error because the rucksack is wrong,
;; transport the object from *my-rucksack* to *other-rucksack*, or
;; multiple Rucksacks per object should be allowed -- in which case
;; later pulling X from *my-rucksack* and updating it should also update
;; the X in *other-rucksack*.
;;
;; A combination of the above:
(with-rucksack (r *my-rucksack*)
(let (x1)
(with-transaction ()
(add-rucksack-root (setf x1 (make-instance 'x))))
(with-rucksack (r *other-rucksack*)
(with-tranaction ()
;; In which Rucksack is X1?
(add-rucksack-root (make-instance 'x :y x1) r))))))
I think there are many valid answers to the questions posed above (and
the implicit questions about P-EQL in the above cases), but I think they
boil down mostly to a meta-question:
Is a single Rucksack a "universe" or a "container"?
If a Rucksack is a universe, then it would make sense to enforce
certain restrictions on that universe: eg. you cannot assign objects
belonging to other universes to persistent slots.
If a Rucksack is seen as a container, then giving both Rucksacks
their own copies would be fine, but so (I think) would be allowing
cross-rucksack references (so that *my-rucksack* could hold a
pointer to an object in *other-rucksack*).
In the universe case there is a relatively simple option that might
untangle some things: make Rucksack a property of a class.
(defclass foo-class (persistent-class) ()
(:default-initargs :rucksack "/var/rucksacks/foo/"))
(defclass foo () (:metaclass foo-class))
or
(defclass bar () () (:metaclass persisten-class) (:rucksack ...))
This would have implications on the whole API of course, but most
that come to mind seem like simplifications -- not all though: eg.
"What to do with PERSISTENT-DATA?"
* Default transactions: are transactions intended to be explicit,
or is the intention that eventually a simple (setf some-slot) would
generate its own transaction unless there already was one?
There is a pleasing clarity to explicit transactions, but they
also mean that persistent objects cannot be manipulated by functions
who don't know they are persistent.
I think in at least 90% of the cases it would be natural for the
called to provide a surrounding transaction contexts, but I'm not
sure how universal that is.
* GC of live-in-lisp but dead-on-disk objects. I should just read the
code, I suppose, but what is the intended result here:
(with-rucksack ...
(let (x)
(with-transaction ()
(setf x (make-instance 'persistent-thing)))
... do enough stuff to cause X to be GC'd from
the file...
(add-rucksack-root x ...)))
Does X get a new object-id, or what?
I realize this is long and long-winded. No rush on any of this: I
mostly just wanted to get this downloaded from my head to list
archives. ;-)
Cheers,
-- Nikodemus Schemer: "Buddha is small, clean, and serious."
Lispnik: "Buddha is big, has hairy armpits, and laughs."
More information about the rucksack-devel
mailing list