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 Marshaled
(bindings, procedure objects, IO instances, or singleton objects).
The transactions supported by Transaction::Simple are not backed
transactions; 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. As with Hash keys, String names will be duplicated
and frozen before using.
Usage
include 'transaction/simple’v = “Hello, you.” # ->
"Hello, you."v.extend(Transaction::Simple) # -> "Hello,
you."v.start_transaction # -> … (a Marshal
string)v.transaction_open? # -> truev.gsub!(/you/,
“world”) # -> "Hello, world."v.rewind_transaction #
-> "Hello, you."v.transaction_open? # -> truev.gsub!(/
you/, “HAL”) # -> "Hello,
HAL."v.abort_transaction # -> "Hello,
you."v.transaction_open? # ->
falsev.start_transaction # -> … (a Marshal
string)v.start_transaction # -> … (a Marshal
string)v.transaction_open? # -> truev.gsub!(/you/,
“HAL”) # -> "Hello, HAL."v.commit_transaction # -
"Hello, HAL."v.transaction_open? # ->
truev.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? # -> truev.transaction_open?
(:first) # -> truev.transaction_open?(:second) # -> falsev.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? # -> truev.transaction_open?
(:first) # -> truev.transaction_open?(:second) # -> falsev.gsub!
(/you/, “world”) # -> "Hello,
world."v.start_transaction(:second) # -> … (a Marshal
string)v.gsub!(/world/, “HAL”) # -> "Hello,
HAL."v.transaction_name # -
:secondv.abort_transaction(:first) # -> "Hello,
you."v.transaction_open? # ->
falsev.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 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? # ->
falseTransaction::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."endv.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? # -> truev.transaction_open?
(:first) # -> truev.transaction_open?(:second) # -> falsev.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? # -> truev.transaction_open?
(:first) # -> truev.transaction_open?(:second) # -> falsev.gsub!
(/you/, “world”) # -> "Hello,
world."v.start_transaction(:second) # -> … (a Marshal
string)v.gsub!(/world/, “HAL”) # -> "Hello,
HAL."v.transaction_name # -
:secondv.abort_transaction(:first) # -> "Hello,
you."v.transaction_open? # ->
falsev.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
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 Marshaled 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 inherently thread-safe. In the ACID (“atomic, consistent,
isolated, durable”) test, Transaction::SimpleTransaction::Simple to
provide isolation and atomicity. Transactions should be considered
“critical sections” in multi-threaded applications. If thread safety
and atomicity is absolutely required, use
Transaction::Simple::ThreadSafe, which uses a Mutex object to
synchronize the accesses on the object during the transaction
operations. provides CD, but it is up to the user of
does not necessarily maintain Object.id values on rewind or abort.
This may change for future versions that will be Ruby 1.8 or better
only. Certain objects that support .replace will maintain Object.id.
Can be a memory hog if you use many levels of transactions on many
objects.
Name Value
SKIP_TRANSACTION_VARS [TRANSACTION_CHECKPOINT,
“@transaction_level”]
TRANSACTION_CHECKPOINT “@transaction_checkpoint”
TRANSACTION_SIMPLE_VERSION ‘1.3.0’
Public Methods
abort_transaction Aborts the transaction. Resets the object state to
what it was before the transaction was started and closes the
transaction. If name is specified, then the intervening transactions
and the named transaction will be aborted. Otherwise, only the current
transaction is aborted.
commit_transaction If name is nil (default), the current transaction
level is closed out and the changes are committed.
debug_io Returns the Transaction::Simple debug object. It must
respond to #<<.
debug_io= Sets the Transaction::Simple debug object. It must respond
to #<<. Sets the transaction debug object. Debugging will be performed
automatically if there’s a debug object. The generic transaction error
class.
debugging? Returns true if we are debugging.
rewind_transaction Rewinds the transaction. If name is specified,
then the intervening transactions will be aborted and the named
transaction will be rewound. Otherwise, only the current transaction
is rewound.
start
start_named
start_transaction Starts a transaction. Stores the current object
state. If a transaction name is specified, the transaction will be
named. Transaction names must be unique. Transaction names of nil will
be treated as unnamed transactions.
transaction Alternative method for calling the transaction methods.
An optional name can be specified for named transaction support.
transaction_exclusions Allows specific variables to be excluded from
transaction support. Must be done after extending the object but
before starting the first transaction on the object.
transaction_name Returns the current name of the transaction.
Transactions not explicitly named are named nil.
transaction_open? If name is nil (default), then returns true if
there is currently a transaction open.
Private Methods
__abort_transaction
__commit_transaction
__common_start
__rewind_this_transaction
Thanks N Regards…
K. Chandrahas R.,
Software Engineer,
Circlesource Software Technologies Pvt Ltd,
Bangalore. Ph: 9986182302