From: John Lam Date: 2005-05-06T04:40:34+09:00 Subject: Re: [ANN] Transaction::Simple 1.3.0 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 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