[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Re: [ANN] Transaction::Simple 1.3.0

John Lam

5/5/2005 6:44:00 PM

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?

Cheers,
-John


________________________________

From: Austin Ziegler [mailto:halostatue@gmail.com]
Sent: Thu 5/5/2005 2:28 PM
To: ruby-talk ML
Subject: [ANN] Transaction::Simple 1.3.0



I am pleased to announce the release of Transaction::Simple 1.3.0.

It can be downloaded from the trans-simple RubyForge project:

http://rubyforge.org/frs/?group_id=295&relea...

It may also be installed as a RubyGem.

= Transaction::Simple for Ruby
Transaction::Simple provides a generic way to add active transaction
support to objects. The transaction methods added by this module will
work with most objects, excluding those that cannot be Marshal-ed
(bindings, procedure objects, IO instances, or singleton objects).

The transactions supported by Transaction::Simple are not backend
transaction; that is, they are not associated with any sort of data
store. They are "live" transactions occurring in memory and in the
object itself. This is to allow "test" changes to be made to an object
before making the changes permanent.

Transaction::Simple can handle an "infinite" number of transaction
levels (limited only by memory). If I open two transactions, commit the
second, but abort the first, the object will revert to the original
version.

Transaction::Simple supports "named" transactions, so that multiple
levels of transactions can be committed, aborted, or rewound by
referring to the appropriate name of the transaction. Names may be any
object except nil.

Version 1.3.0 of Transaction::Simple adds transaction groups. A
transaction group is an object wrapper that manages a group of objects
as if they were a single object for the purpose of transaction
management. All transactions for this group of objects should be
performed against the transaction group object, not against individual
objects in the group.

Copyright: Copyright (c) 2003 - 2005 by Austin Ziegler
Version: 1.3.0
Licence: MIT-Style

Thanks to David Black and Mauricio Fern?ndez for their help with this
library.

== Usage
include 'transaction/simple'

v = "Hello, you." # -> "Hello, you."
v.extend(Transaction::Simple) # -> "Hello, you."

v.start_transaction # -> ... (a Marshal string)
v.transaction_open? # -> true
v.gsub!(/you/, "world") # -> "Hello, world."

v.rewind_transaction # -> "Hello, you."
v.transaction_open? # -> true

v.gsub!(/you/, "HAL") # -> "Hello, HAL."
v.abort_transaction # -> "Hello, you."
v.transaction_open? # -> false

v.start_transaction # -> ... (a Marshal string)
v.start_transaction # -> ... (a Marshal string)

v.transaction_open? # -> true
v.gsub!(/you/, "HAL") # -> "Hello, HAL."

v.commit_transaction # -> "Hello, HAL."
v.transaction_open? # -> true
v.abort_transaction # -> "Hello, you."
v.transaction_open? # -> false

== Named Transaction Usage
v = "Hello, you." # -> "Hello, you."
v.extend(Transaction::Simple) # -> "Hello, you."

v.start_transaction(:first) # -> ... (a Marshal string)
v.transaction_open? # -> true
v.transaction_open?(:first) # -> true
v.transaction_open?(:second) # -> false
v.gsub!(/you/, "world") # -> "Hello, world."

v.start_transaction(:second) # -> ... (a Marshal string)
v.gsub!(/world/, "HAL") # -> "Hello, HAL."
v.rewind_transaction(:first) # -> "Hello, you."
v.transaction_open? # -> true
v.transaction_open?(:first) # -> true
v.transaction_open?(:second) # -> false

v.gsub!(/you/, "world") # -> "Hello, world."
v.start_transaction(:second) # -> ... (a Marshal string)
v.gsub!(/world/, "HAL") # -> "Hello, HAL."
v.transaction_name # -> :second
v.abort_transaction(:first) # -> "Hello, you."
v.transaction_open? # -> false

v.start_transaction(:first) # -> ... (a Marshal string)
v.gsub!(/you/, "world") # -> "Hello, world."
v.start_transaction(:second) # -> ... (a Marshal string)
v.gsub!(/world/, "HAL") # -> "Hello, HAL."

v.commit_transaction(:first) # -> "Hello, HAL."
v.transaction_open? # -> false

== Block Transaction Usage
v = "Hello, you." # -> "Hello, you."
Transaction::Simple.start(v) do |tv|
# v has been extended with Transaction::Simple and an unnamed
# transaction has been started.
tv.transaction_open? # -> true
tv.gsub!(/you/, "world") # -> "Hello, world."

tv.rewind_transaction # -> "Hello, you."
tv.transaction_open? # -> true

tv.gsub!(/you/, "HAL") # -> "Hello, HAL."
# The following breaks out of the transaction block after
# aborting the transaction.
tv.abort_transaction # -> "Hello, you."
end
# v still has Transaction::Simple applied from here on out.
v.transaction_open? # -> false

Transaction::Simple.start(v) do |tv|
tv.start_transaction # -> ... (a Marshal string)

tv.transaction_open? # -> true
tv.gsub!(/you/, "HAL") # -> "Hello, HAL."

# If #commit_transaction were called without having started a
# second transaction, then it would break out of the transaction
# block after committing the transaction.
tv.commit_transaction # -> "Hello, HAL."
tv.transaction_open? # -> true
tv.abort_transaction # -> "Hello, you."
end
v.transaction_open? # -> false

== Transaction Groups
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

x.gsub!(/you/, "world") # -> "Hello, world."
y.gsub!(/you/, "me") # -> "And me, too."

g.start_transaction(:second) # -> [ x, y ]
x.gsub!(/world/, "HAL") # -> "Hello, HAL."
y.gsub!(/me/, "Dave") # -> "And Dave, too."

g.rewind_transaction(:second) # -> [ x, y ]
x # -> "Hello, world."
y # -> "And me, too."

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."

g.abort_transaction(:first) # -> [ x, y ]
x = -> "Hello, you."
y = -> "And you, too."

== Thread Safety
Threadsafe version of Transaction::Simple and Transaction::Simple::Group
exist; these are loaded from 'transaction/simple/threadsafe' and
'transaction/simple/threadsafe/group', respectively, and are represented
in Ruby code as Transaction::Simple::ThreadSafe and
Transaction::Simple::ThreadSafe::Group, respectively.

== Contraindications
While Transaction::Simple is very useful, it has some severe limitations
that must be understood. Transaction::Simple:

* uses Marshal. Thus, any object which cannot be Marshal-ed cannot use
Transaction::Simple. In my experience, this affects singleton objects
more often than any other object. It may be that Ruby 2.0 will solve
this problem.
* does not manage resources. Resources external to the object and its
instance variables are not managed at all. However, all instance
variables and objects "belonging" to those instance variables are
managed. If there are object reference counts to be handled,
Transaction::Simple will probably cause problems.
* is not thread-safe. In the ACID ("atomic, consistent, isolated,
durable") test, Transaction::Simple provides C and D, but it is up to
the user of Transaction::Simple to provide isolation. Transactions
should be considered "critical sections" in multi-threaded
applications. Thread safety can be ensured with
Transaction::Simple::ThreadSafe. With transaction groups, some level
of atomicity is assured.
* does not maintain Object#__id__ values on rewind or abort. This may
change for future versions.

== Transaction::simple 1.3.0
* Updated to fix a lot of warnings.
* Added a per-transaction-object list of excluded instance variables.
* Moved Transaction::simple::ThreadSafe to transaction/simple/threadsafe.
* Added transaction groups. Transaction groups are wrapper objects to allow
the coordination of transactions with a group of objects. There are both
normal and threadsafe versions of transaction groups.
* Fixed a long-standing problem where instance variables that were added to an
object after a transaction was started would remain.
* Reorganised unit tests.

-austin
--
Austin Ziegler * halostatue@gmail.com
* Alternate: austin@halostatue.ca






1 Answer

Austin Ziegler

5/5/2005 7:17:00 PM

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