Skip to content

Commit a078c31

Browse files
authored
chore: Refactor and optimize MySQL advisory lock handling (#98)
1 parent 3ab0026 commit a078c31

File tree

6 files changed

+52
-13
lines changed

6 files changed

+52
-13
lines changed

Makefile

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
.PHONY: test-pg test-mysql
2+
3+
test-pg:
4+
docker compose up -d pg
5+
sleep 10 # give some time for the service to start
6+
DATABASE_URL=postgres://with_advisory:with_advisory_pass@localhost/with_advisory_lock_test appraisal rake test
7+
8+
test-mysql:
9+
docker compose up -d mysql
10+
sleep 10 # give some time for the service to start
11+
DATABASE_URL=mysql2://with_advisory:[email protected]:3306/with_advisory_lock_test appraisal rake test
12+
13+
14+
test: test-pg test-mysql

docker-compose.yml

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
version: "3.9"
2+
services:
3+
pg:
4+
image: postgres:16
5+
environment:
6+
POSTGRES_USER: with_advisory
7+
POSTGRES_PASSWORD: with_advisory_pass
8+
POSTGRES_DB: with_advisory_lock_test
9+
ports:
10+
- "5432:5432"
11+
mysql:
12+
image: mysql:8
13+
environment:
14+
MYSQL_USER: with_advisory
15+
MYSQL_PASSWORD: with_advisory_pass
16+
MYSQL_DATABASE: with_advisory_lock_test
17+
MYSQL_RANDOM_ROOT_PASSWORD: "yes"
18+
MYSQL_ROOT_HOST: '%'
19+
ports:
20+
- "3306:3306"

lib/with_advisory_lock/mysql.rb

+8-6
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
module WithAdvisoryLock
44
class MySQL < Base
5-
# Caches nested lock support by MySQL reported version
6-
@@mysql_nl_cache = {}
7-
@@mysql_nl_cache_mutex = Mutex.new
8-
# See https://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_get-lock
5+
# See https://dev.mysql.com/doc/refman/5.7/en/locking-functions.html
6+
# See https://dev.mysql.com/doc/refman/8.0/en/locking-functions.html
97
def try_lock
108
raise ArgumentError, 'shared locks are not supported on MySQL' if shared
119
raise ArgumentError, 'transaction level locks are not supported on MySQL' if transaction
@@ -18,8 +16,12 @@ def release_lock
1816
end
1917

2018
def execute_successful?(mysql_function)
21-
sql = "SELECT #{mysql_function} AS #{unique_column_name}"
22-
connection.select_value(sql).to_i.positive?
19+
execute_query(mysql_function) == 1
20+
end
21+
22+
def execute_query(mysql_function)
23+
sql = "SELECT #{mysql_function}"
24+
connection.query_value(sql)
2325
end
2426

2527
# MySQL wants a string as the lock key.

test/test_helper.rb

+6-3
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,11 @@ def is_postgresql_adapter?; adapter_support.postgresql?; end
4949

5050
setup do
5151
ENV['FLOCK_DIR'] = Dir.mktmpdir if is_sqlite3_adapter?
52-
Tag.delete_all
53-
TagAudit.delete_all
54-
Label.delete_all
52+
ApplicationRecord.connection.truncate_tables(
53+
Tag.table_name,
54+
TagAudit.table_name,
55+
Label.table_name
56+
)
5557
end
5658

5759
teardown do
@@ -60,3 +62,4 @@ def is_postgresql_adapter?; adapter_support.postgresql?; end
6062
end
6163

6264
puts "Testing with #{env_db} database, ActiveRecord #{ActiveRecord.gem_version} and #{RUBY_ENGINE} #{RUBY_ENGINE_VERSION} as #{RUBY_VERSION}"
65+
puts "Connection Pool size: #{ActiveRecord::Base.connection_pool.size}"

test/with_advisory_lock/shared_test.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def cleanup!
2424
private
2525

2626
def work
27-
ApplicationRecord.connection_pool.with_connection do
27+
Tag.connection_pool.with_connection do
2828
Tag.with_advisory_lock('test', timeout_seconds: 0, shared: @shared) do
2929
@locked = true
3030
sleep 0.01 until @cleanup
@@ -117,7 +117,7 @@ class PostgreSQLTest < SupportedEnvironmentTest
117117
end
118118

119119
def pg_lock_modes
120-
ApplicationRecord.connection.select_values("SELECT mode FROM pg_locks WHERE locktype = 'advisory';")
120+
Tag.connection.select_values("SELECT mode FROM pg_locks WHERE locktype = 'advisory';")
121121
end
122122

123123
test 'allows shared lock to be upgraded to an exclusive lock' do

test/with_advisory_lock/thread_test.rb

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class SeparateThreadTest < GemTestCase
1010
@t1_return_value = nil
1111

1212
@t1 = Thread.new do
13-
ApplicationRecord.connection_pool.with_connection do
13+
Label.connection_pool.with_connection do
1414
@t1_return_value = Label.with_advisory_lock(@lock_name) do
1515
@mutex.synchronize { @t1_acquired_lock = true }
1616
sleep
@@ -21,7 +21,7 @@ class SeparateThreadTest < GemTestCase
2121

2222
# Wait for the thread to acquire the lock:
2323
sleep(0.1) until @mutex.synchronize { @t1_acquired_lock }
24-
ApplicationRecord.connection.reconnect!
24+
Label.connection.reconnect!
2525
end
2626

2727
teardown do

0 commit comments

Comments
 (0)