John Lam
5/5/2005 7:41:00 PM
Yes - what you're saying does make sense.
I was thinking about cases where you're performing transactions against large objects, say a hash table.
Since the target objects don't know anything about transactions (and you don't know anything about their internal state) then this would wind up duplicating a lot of state.
However, if an object were Tx aware, then it would know how to manage its state in face of a transaction manager. Adding a single entry to a hash table wouldn't require copying the entire table first - you'd simply record the fact that you are adding a record and add it at commit-time.
At this point you'll need to have 2-phase commit protocols - telling each object to prepare to commit before actually committing. Any object that fails to prepare to commit aborts the entire Tx.
This is probably a lot more complicated than what you have envisioned (after all these are *Simple* transactions). I think this will still be very useful for many situations where you aren't modifying very large objects (or are not doing many such Tx in parallel).
-John
________________________________
From: Austin Ziegler [mailto:halostatue@gmail.com]
Sent: Thu 5/5/2005 3:17 PM
To: ruby-talk ML
Subject: Re: [ANN] Transaction::Simple 1.3.0
On 5/5/05, John Lam <jlam@iunknown.com> wrote:
> Wow wow wow! This is so massively cool. Thanks for creating this.
> I can't wait to spend some quality time with this.
>
> Do you snapshot state as the objects are modified? If so, does
> this avoid the problem of requiring two phase commit in your
> transaction groups?
I'm not quite sure what you're saying here. Transaction snapshots
are taken when you start the transaction:
require 'transaction/simple/group'
x = "Hello, you."
y = "And you, too."
g = Transaction::Simple::Group.new(x, y)
g.start_transaction(:first) # -> [ x, y ]
g.transaction_open?(:first) # -> true
x.transaction_open?(:first) # -> true
y.transaction_open?(:first) # -> true
At this point, the transaction state of x and y is set. 'g' doesn't
maintain any transaction state; x and y do independently. Strictly
speaking, the transaction group is just a synchronizer.
x.gsub!(/you/, "world") # -> "Hello, world."
y.gsub!(/you/, "me") # -> "And me, too."
Here, we've modified both x and y. No new snapshot has yet been
made.
g.start_transaction(:second) # -> [ x, y ]
There. That takes another snapshot and gives it the transaction name
of :second.
x.gsub!(/world/, "HAL") # -> "Hello, HAL."
y.gsub!(/me/, "Dave") # -> "And Dave, too."
Another modification.
g.rewind_transaction(:second) # -> [ x, y ]
x # -> "Hello, world."
y # -> "And me, too."
We've rewound the transaction on each object to its original state
here.
x.gsub!(/world/, "HAL") # -> "Hello, HAL."
y.gsub!(/me/, "Dave") # -> "And Dave, too."
g.commit_transaction(:second) # -> [ x, y ]
x # -> "Hello, HAL."
y # -> "And Dave, too."
Changes are made and the second transaction is committed on both
objects. Note: if something goes wrong in the committing of the
first object, then the second object won't be committed.
g.abort_transaction(:first) # -> [ x, y ]
x = -> "Hello, you."
y = -> "And you, too."
This aborts the transaction and restores the state of the objects to
their original value. Except that they are now able to do
transactions.
Make sense?
Most of this code is based on what I did for block transactions:
Transaction::Simple.start_named(:foo, x, y) do |tx, ty|
...
end
I may go through at some point and unify the implementations here
and provide block forms of the transaction group methods.
-austin
--
Austin Ziegler * halostatue@gmail.com
* Alternate: austin@halostatue.ca