sequel-advisory-locking 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57fd6955d93c47e0e7c2be787616466d8e87e351
|
4
|
+
data.tar.gz: d35d690106eb1d21f4dea2624abad06600c4b9d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cce1a093220e70c2758d78d59920c60627de1d040742c2a4e69641f78d6a115ce40385768da9e7e9773c0fd62e335e4dd4d0f865d1286930dd5e09105936b94e
|
7
|
+
data.tar.gz: 1321d4964ebca516d48f8ab06999a13b8db69bba80d3f82119110d402bd01fb5cc6b4e00a31eb24c54fc4210f76e17c8600c914d1c016034ac9027297240548d
|
@@ -8,45 +8,77 @@ module Sequel
|
|
8
8
|
module AdvisoryLocking
|
9
9
|
class Error < StandardError; end
|
10
10
|
|
11
|
-
HEX_STRING_SLICE_RANGE = 0..15
|
11
|
+
HEX_STRING_SLICE_RANGE = (0..15).freeze
|
12
12
|
POSTGRES_SIGNED_BIGINT_BOUND = 2**63
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def advisory_lock(key, try: false)
|
20
|
-
int =
|
21
|
-
when Integer then key
|
22
|
-
when String, Symbol
|
23
|
-
# For an arbitrary string, pseudorandomly return an integer in
|
24
|
-
# the PG bigint range.
|
25
|
-
hex = Digest::MD5.hexdigest(key.to_s)[HEX_STRING_SLICE_RANGE].hex
|
26
|
-
# Mimic PG's bigint rollover behavior.
|
27
|
-
hex -= POSTGRES_UNSIGNED_BIGINT_RANGE if hex >= POSTGRES_SIGNED_BIGINT_BOUND
|
28
|
-
hex
|
29
|
-
else
|
30
|
-
raise Error, "passed an invalid key type (#{key.class})"
|
31
|
-
end
|
13
|
+
POSTGRES_UNSIGNED_BIGINT_BOUND = 2**64
|
14
|
+
|
15
|
+
POSTGRES_SIGNED_BIGINT_MAXIMUM = POSTGRES_SIGNED_BIGINT_BOUND - 1
|
16
|
+
POSTGRES_SIGNED_BIGINT_MINIMUM = -POSTGRES_SIGNED_BIGINT_BOUND
|
17
|
+
POSTGRES_SIGNED_BIGINT_RANGE = (POSTGRES_SIGNED_BIGINT_MINIMUM..POSTGRES_SIGNED_BIGINT_MAXIMUM).freeze
|
18
|
+
|
19
|
+
def advisory_lock(key, try: false, shared: false, &block)
|
20
|
+
int = advisory_lock_key(key)
|
32
21
|
|
33
22
|
synchronize do
|
34
23
|
begin
|
35
24
|
# Add key to the end so that logs read easier.
|
36
|
-
sql = try ?
|
25
|
+
sql = "SELECT pg#{'_try' if try}_advisory_lock#{'_shared' if shared}(?) -- ?"
|
37
26
|
locked = !!self[sql, int, key].get
|
38
27
|
|
39
|
-
if locked &&
|
40
|
-
|
28
|
+
if locked && block
|
29
|
+
if in_transaction?
|
30
|
+
# If we're in a transaction and an error occurs at the DB level,
|
31
|
+
# the advisory lock won't be released for us and we won't be
|
32
|
+
# able to run the unlock function below. So, wrap the block in a
|
33
|
+
# savepoint that will hopefully be transparent to the caller.
|
34
|
+
transaction(savepoint: true, rollback: :reraise, &block)
|
35
|
+
else
|
36
|
+
# If we're not in a transaction, of course, we don't have that
|
37
|
+
# worry, and we don't want to force the caller to enter a
|
38
|
+
# transaction that they maybe don't want to incur the overhead
|
39
|
+
# of, so just yield.
|
40
|
+
yield
|
41
|
+
end
|
41
42
|
else
|
42
43
|
locked
|
43
44
|
end
|
44
45
|
ensure
|
45
|
-
|
46
|
+
sql = "SELECT pg_advisory_unlock#{'_shared' if shared}(?) -- ?"
|
47
|
+
self[sql, int, key].get if locked
|
46
48
|
end
|
47
49
|
end
|
48
50
|
end
|
51
|
+
|
52
|
+
def advisory_lock_key(key)
|
53
|
+
case key
|
54
|
+
when Integer
|
55
|
+
advisory_lock_key_range_check(key)
|
56
|
+
when String, Symbol
|
57
|
+
# For an arbitrary string, pseudorandomly return an integer in
|
58
|
+
# the PG bigint range.
|
59
|
+
hex = Digest::MD5.hexdigest(key.to_s)[HEX_STRING_SLICE_RANGE].hex
|
60
|
+
|
61
|
+
# Mimic PG's bigint rollover behavior.
|
62
|
+
hex -= POSTGRES_UNSIGNED_BIGINT_BOUND if hex >= POSTGRES_SIGNED_BIGINT_BOUND
|
63
|
+
|
64
|
+
# The keys we derive from strings shouldn't ever fall outside the
|
65
|
+
# bigint range, but assert that just to be safe.
|
66
|
+
advisory_lock_key_range_check(hex)
|
67
|
+
else
|
68
|
+
raise Error, "passed an invalid key type (#{key.class})"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def advisory_lock_key_range_check(integer)
|
75
|
+
if POSTGRES_SIGNED_BIGINT_RANGE.cover?(integer)
|
76
|
+
integer
|
77
|
+
else
|
78
|
+
raise Error, "given advisory lock integer (#{integer}) falls outside Postgres' bigint range"
|
79
|
+
end
|
80
|
+
end
|
49
81
|
end
|
50
82
|
|
51
|
-
Database.register_extension(:advisory_locking){|db| db.extend(Sequel::AdvisoryLocking)}
|
83
|
+
Database.register_extension(:advisory_locking) { |db| db.extend(Sequel::AdvisoryLocking) }
|
52
84
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sequel-advisory-locking
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Hanks
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-06-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|