Skip to content

Commit ba34b5e

Browse files
Merge pull request #60 from bitcrowd/fix/advisory-xact-lock-should-accept-atoms
Fix Repo.advisory_xact_lock/1 to handle atom names
2 parents 024b790 + 20f39f8 commit ba34b5e

2 files changed

Lines changed: 46 additions & 6 deletions

File tree

lib/bitcrowd_ecto/repo.ex

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ defmodule BitcrowdEcto.Repo do
137137
"application" key and one specific lock key), in which case PostgreSQL concatenates them
138138
into a 64-bit value. In any case you need to pass integers.
139139
140-
We decided that we wanted to have atom or string keys for better readability. Hence, in=
140+
We decided that we wanted to have atom or string keys for better readability. Hence, in
141141
order to make PostgreSQL happy, we hash these strings into 64 bits signed ints.
142142
143143
64 bits make a pretty big number already, so it is quite unlikely that two of our keys (or
@@ -254,8 +254,8 @@ defmodule BitcrowdEcto.Repo do
254254
end
255255

256256
@doc false
257-
def advisory_xact_lock(repo, name) do
258-
<<advisory_lock_key::signed-integer-64, _rest::binary>> = :crypto.hash(:sha, name)
257+
def advisory_xact_lock(repo, name) when is_atom(name) or is_binary(name) do
258+
<<advisory_lock_key::signed-integer-64, _rest::binary>> = :crypto.hash(:sha, to_string(name))
259259
SQL.query!(repo, "SELECT pg_advisory_xact_lock($1);", [advisory_lock_key])
260260
:ok
261261
end

test/bitcrowd_ecto/repo_test.exs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,13 +102,16 @@ defmodule BitcrowdEcto.RepoTest do
102102
assert TestRepo.fetch_by(query, []) == {:ok, resource}
103103
end
104104

105-
# The actual lock is non-trivial to test, I tried.
106105
test "can lock for :update", %{resource: %{id: id} = resource} do
107-
assert TestRepo.fetch_by(TestSchema, [id: id], lock: :update) == {:ok, resource}
106+
assert_lock_granted("relation = 'test_schema_pkey'::regclass::oid", fn ->
107+
assert TestRepo.fetch_by(TestSchema, [id: id], lock: :update) == {:ok, resource}
108+
end)
108109
end
109110

110111
test "can lock for :no_key_update", %{resource: %{id: id} = resource} do
111-
assert TestRepo.fetch_by(TestSchema, [id: id], lock: :no_key_update) == {:ok, resource}
112+
assert_lock_granted("relation = 'test_schema_pkey'::regclass::oid", fn ->
113+
assert TestRepo.fetch_by(TestSchema, [id: id], lock: :no_key_update) == {:ok, resource}
114+
end)
112115
end
113116

114117
test "converts CastErrors for binary_id columns to not_found errors" do
@@ -161,4 +164,41 @@ defmodule BitcrowdEcto.RepoTest do
161164
assert TestRepo.fetch_by(TestSchema, [id: resource.id], prefix: prefix) == {:ok, resource}
162165
end
163166
end
167+
168+
describe "advisory_xact_lock/1" do
169+
test "acquires an advisory lock" do
170+
assert_lock_granted("locktype = 'advisory'", fn ->
171+
TestRepo.advisory_xact_lock("foo")
172+
end)
173+
end
174+
175+
test "accepts atoms" do
176+
assert_lock_granted("locktype = 'advisory'", fn ->
177+
TestRepo.advisory_xact_lock(:foo)
178+
end)
179+
end
180+
end
181+
182+
defp assert_lock_granted(where, fun) do
183+
assert locks_granted(where) == 0
184+
result = fun.()
185+
assert locks_granted(where) == 1
186+
result
187+
end
188+
189+
defp locks_granted(where) do
190+
%{rows: [[vxid]]} =
191+
TestRepo.query!("""
192+
SELECT virtualtransaction FROM pg_locks
193+
WHERE transactionid::text = (txid_current() % (2^32)::bigint)::text;
194+
""")
195+
196+
%{rows: [[count]]} =
197+
TestRepo.query!("""
198+
SELECT COUNT(*) FROM pg_locks
199+
WHERE virtualtransaction = '#{vxid}' AND granted IS TRUE AND #{where};
200+
""")
201+
202+
count
203+
end
164204
end

0 commit comments

Comments
 (0)