Skip to content

Commit

Permalink
Add :disconnect=>:retry option to Database#transaction, for automatic…
Browse files Browse the repository at this point in the history
…ally retrying the transaction on disconnect

This has been requested numerous times over the years.  I think its
a bad idea, but it's possible that it is the best alternative when
dealing with a particularly bad database configuration.

Basically, automatically retrying can cause all sorts of problems.
The most likely way of getting it working is to only retry
at transaction boundries.  For a sane database that rolls back
transactions on disconnect, this should work OK for pure database
code.  However, if you do any non-database work inside the
transaction block, you need to be sure that that the work is
idempotent in addition to making sure that it handles rollbacks
correctly.
  • Loading branch information
jeremyevans committed Sep 10, 2012
1 parent 2b4a5df commit 41ab298
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 3 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
=== HEAD

* Add :disconnect=>:retry option to Database#transaction, for automatically retrying the transaction on disconnect (jeremyevans)

* Greatly improved support on Microsoft Access (jeremyevans)

* Support Database#{schema,tables,views,indexes,foreign_key_list} when using ado/access adapter (ericgj) (#545, #546)
Expand Down
27 changes: 24 additions & 3 deletions lib/sequel/database/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,14 @@ def tables(opts={})
#
# The following general options are respected:
#
# :disconnect :: Can be set to :retry to automatically retry the transaction with
# a new connection object if it detects a disconnect on the connection.
# Note that this should not be used unless the entire transaction
# block is idempotent, as otherwise it can cause non-idempotent
# behavior to execute multiple times. This does no checking for
# infinite loops, so if your transaction will repeatedly raise a
# disconnection error, this will cause the transaction block to loop
# indefinitely.
# :isolation :: The transaction isolation level to use for this transaction,
# should be :uncommitted, :committed, :repeatable, or :serializable,
# used if given and the database/adapter supports customizable
Expand All @@ -258,9 +266,22 @@ def tables(opts={})
# appropriately. Valid values true, :on, false, :off, :local (9.1+),
# and :remote_write (9.2+).
def transaction(opts={}, &block)
synchronize(opts[:server]) do |conn|
return yield(conn) if already_in_transaction?(conn, opts)
_transaction(conn, opts, &block)
if opts[:disconnect] == :retry
begin
transaction(opts.merge(:disconnect=>:disallow), &block)
rescue Sequel::DatabaseDisconnectError
retry
end
else
synchronize(opts[:server]) do |conn|
if already_in_transaction?(conn, opts)
if opts[:disconnect] == :disallow
raise Sequel::Error, "cannot set :disconnect=>:retry if you are already inside a transaction"
end
return yield(conn)
end
_transaction(conn, opts, &block)
end
end
end

Expand Down
12 changes: 12 additions & 0 deletions spec/core/database_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,18 @@ def @o.to_ary; [self]; end
'BEGIN', 'SET TRANSACTION ISOLATION LEVEL SERIALIZABLE', 'DROP TABLE serializable', 'COMMIT']
end

specify "should support :disconnect=>:retry option for automatically retrying on disconnect" do
a = []
@db.transaction(:disconnect=>:retry){a << 1; raise Sequel::DatabaseDisconnectError if a.length < 2}
@db.sqls.should == ['BEGIN', 'ROLLBACK', 'BEGIN', 'COMMIT']
a.should == [1, 1]
end

specify "should raise an error if attempting to use :disconnect=>:retry inside another transaction" do
proc{@db.transaction{@db.transaction(:disconnect=>:retry){}}}.should raise_error(Sequel::Error)
@db.sqls.should == ['BEGIN', 'ROLLBACK']
end

specify "should handle returning inside of the block by committing" do
def @db.ret_commit
transaction do
Expand Down

0 comments on commit 41ab298

Please sign in to comment.