em-pg-client-helper 0.5.1 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/em-pg-client-helper/transaction.rb +28 -3
- data/spec/db_transaction_spec.rb +53 -9
- data/spec/spec_helper.rb +1 -1
- metadata +3 -3
@@ -10,26 +10,51 @@ class PG::EM::Client::Helper::Transaction
|
|
10
10
|
# This can be `nil` if the txn is in progress, or it will be
|
11
11
|
# true or false to indicate success/failure of the txn
|
12
12
|
@committed = nil
|
13
|
+
@retryable = false
|
13
14
|
|
14
15
|
DeferrableGroup.new do |dg|
|
15
16
|
@dg = dg
|
16
17
|
|
17
18
|
trace_query("BEGIN")
|
18
|
-
@conn.exec_defer("BEGIN").
|
19
|
+
@conn.exec_defer("BEGIN").tap do |df|
|
20
|
+
@dg.add(df)
|
21
|
+
end.callback do
|
19
22
|
begin
|
20
23
|
blk.call(self)
|
21
24
|
rescue StandardError => ex
|
22
25
|
rollback(ex)
|
23
26
|
end
|
24
|
-
end.errback
|
27
|
+
end.errback do |ex|
|
28
|
+
rollback(ex)
|
29
|
+
end
|
25
30
|
end.callback do
|
26
31
|
rollback(RuntimeError.new("txn.commit was not called")) unless @committed
|
27
32
|
self.succeed
|
28
33
|
end.errback do |ex|
|
29
|
-
|
34
|
+
if @retryable and [PG::TRSerializationFailure].include?(ex.class)
|
35
|
+
self.class.new(conn, opts, &blk).callback do
|
36
|
+
self.succeed
|
37
|
+
end.errback do |ex|
|
38
|
+
self.fail(ex)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
self.fail(ex)
|
42
|
+
end
|
30
43
|
end
|
31
44
|
end
|
32
45
|
|
46
|
+
# Mark the transaction as requiring the serializable isolation level.
|
47
|
+
#
|
48
|
+
# @param retryable [TrueClass, FalseClass] Whether or not the transaction
|
49
|
+
# should be retried if some sort of transaction-level failure occurs.
|
50
|
+
# Be careful enabling this, as the entire block will be re-run, including
|
51
|
+
# any code that creates side-effects elsewhere.
|
52
|
+
#
|
53
|
+
def serializable(retryable = false, &blk)
|
54
|
+
@retryable = retryable
|
55
|
+
exec("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", &blk)
|
56
|
+
end
|
57
|
+
|
33
58
|
# Signal the database to commit this transaction. You must do this
|
34
59
|
# once you've completed your queries, it won't be called automatically
|
35
60
|
# for you. Once you've committed the transaction, you cannot use it
|
data/spec/db_transaction_spec.rb
CHANGED
@@ -3,21 +3,22 @@ require_relative './spec_helper'
|
|
3
3
|
describe "PG::EM::Client::Helper#db_transaction" do
|
4
4
|
let(:mock_conn) { double(PG::EM::Client) }
|
5
5
|
|
6
|
-
def expect_query_failure(q, args=nil, exec_time = 0.001)
|
7
|
-
|
6
|
+
def expect_query_failure(q, args=nil, err=nil, exec_time = 0.001)
|
7
|
+
err ||= RuntimeError.new("Dummy failure")
|
8
|
+
expect_query(q, args, exec_time, :fail, err)
|
8
9
|
end
|
9
10
|
|
10
|
-
def expect_query(q, args=nil, exec_time = 0.001, disposition = :succeed)
|
11
|
+
def expect_query(q, args=nil, exec_time = 0.001, disposition = :succeed, *disp_opts)
|
11
12
|
df = EM::DefaultDeferrable.new
|
12
13
|
|
13
|
-
|
14
|
-
to receive(:exec_defer)
|
15
|
-
with(*[q, args].compact)
|
16
|
-
and_return(df)
|
17
|
-
ordered
|
14
|
+
expect(mock_conn)
|
15
|
+
.to receive(:exec_defer)
|
16
|
+
.with(*[q, args].compact)
|
17
|
+
.and_return(df)
|
18
|
+
.ordered
|
18
19
|
|
19
20
|
EM.add_timer(exec_time) do
|
20
|
-
df.__send__(disposition)
|
21
|
+
df.__send__(disposition, *disp_opts)
|
21
22
|
end
|
22
23
|
end
|
23
24
|
|
@@ -158,4 +159,47 @@ describe "PG::EM::Client::Helper#db_transaction" do
|
|
158
159
|
end
|
159
160
|
end
|
160
161
|
end
|
162
|
+
|
163
|
+
it "retries if it gets an error during the transaction" do
|
164
|
+
in_em do
|
165
|
+
expect_query("BEGIN")
|
166
|
+
expect_query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", [])
|
167
|
+
expect_query_failure('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"], PG::TRSerializationFailure.new("OMFG!"))
|
168
|
+
expect_query("ROLLBACK")
|
169
|
+
expect_query("BEGIN")
|
170
|
+
expect_query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", [])
|
171
|
+
expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"])
|
172
|
+
expect_query("COMMIT")
|
173
|
+
|
174
|
+
in_transaction do |txn|
|
175
|
+
txn.serializable(true) do
|
176
|
+
txn.insert("foo", :bar => 'baz') do
|
177
|
+
txn.commit
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
it "retries if it gets an error on commit" do
|
185
|
+
in_em do
|
186
|
+
expect_query("BEGIN")
|
187
|
+
expect_query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", [])
|
188
|
+
expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"])
|
189
|
+
expect_query_failure("COMMIT", nil, PG::TRSerializationFailure.new("OMFG!"))
|
190
|
+
expect_query("ROLLBACK")
|
191
|
+
expect_query("BEGIN")
|
192
|
+
expect_query("SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", [])
|
193
|
+
expect_query('INSERT INTO "foo" ("bar") VALUES ($1)', ["baz"])
|
194
|
+
expect_query("COMMIT")
|
195
|
+
|
196
|
+
in_transaction do |txn|
|
197
|
+
txn.serializable(true) do
|
198
|
+
txn.insert("foo", :bar => 'baz') do
|
199
|
+
txn.commit
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
161
205
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: em-pg-client-helper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -261,7 +261,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
261
261
|
version: '0'
|
262
262
|
segments:
|
263
263
|
- 0
|
264
|
-
hash: -
|
264
|
+
hash: -3912850109456819706
|
265
265
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
266
266
|
none: false
|
267
267
|
requirements:
|
@@ -270,7 +270,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
270
270
|
version: '0'
|
271
271
|
segments:
|
272
272
|
- 0
|
273
|
-
hash: -
|
273
|
+
hash: -3912850109456819706
|
274
274
|
requirements: []
|
275
275
|
rubyforge_project:
|
276
276
|
rubygems_version: 1.8.23
|