Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(clean): use postgres advisory locks to ensure only one process can run a clean at a time #672

Merged
merged 14 commits into from
Mar 27, 2024
Prev Previous commit
Next Next commit
style: rubocop
bethesque committed Mar 26, 2024
commit 23af86198b3401964039198fcd50797d70b3731b
8 changes: 4 additions & 4 deletions lib/sequel/extensions/pg_advisory_lock.rb
Original file line number Diff line number Diff line change
@@ -4,9 +4,9 @@
# should not cause a Sequel::Error to be raised.
# Also, I wanted it to use Concurrent::Hash for multi-threaded environments.

require 'sequel'
require 'zlib'
require 'concurrent/hash'
require "sequel"
require "zlib"
require "concurrent/hash"

module Sequel
module Postgres
@@ -33,7 +33,7 @@ def registered_advisory_locks
@registered_advisory_locks ||= Concurrent::Hash.new
end

def with_advisory_lock(name, id = nil, &block)
def with_advisory_lock(name, id = nil)
options = registered_advisory_locks.fetch(name.to_sym)

lock_key = options.fetch(:key)
18 changes: 9 additions & 9 deletions spec/lib/sequel/extensions/register_advisory_lock_test.rb
Original file line number Diff line number Diff line change
@@ -3,7 +3,7 @@
describe Sequel::Postgres::PgAdvisoryLock do
subject { Sequel::Model.db }

describe '#register_advisory_lock' do
describe "#register_advisory_lock" do
let(:supported_lock_functions) do
[
:pg_advisory_lock,
@@ -23,15 +23,15 @@
subject.registered_advisory_locks.clear
end

it 'base check' do
it "base check" do
lock_name = :test_lock

expect(subject.registered_advisory_locks[lock_name]).to be nil
subject.register_advisory_lock(lock_name)
expect(default_lock_function).to eq subject.registered_advisory_locks[lock_name].fetch(:lock_function)
end

it 'should register locks for all supported PostgreSQL functions' do
it "should register locks for all supported PostgreSQL functions" do
supported_lock_functions.each do |lock_function|
lock_name = "#{lock_function}_test".to_sym

@@ -41,28 +41,28 @@
end
end

it 'should prevent specifying not supported PostgreSQL function as lock type' do
it "should prevent specifying not supported PostgreSQL function as lock type" do
lock_name = :not_supported_lock_function_test
lock_function = :not_supported_lock_function

expect { subject.register_advisory_lock(lock_name, lock_function) }.to raise_error(Sequel::Error, /Invalid lock function/)
end

it 'should prevent registering multiple locks with same name and different functions' do
it "should prevent registering multiple locks with same name and different functions" do
lock_name = :multiple_locks_with_same_name_test
subject.register_advisory_lock(lock_name, supported_lock_functions[0])

expect { subject.register_advisory_lock(lock_name, supported_lock_functions[1]) }.to raise_error(Sequel::Error, /Lock with name .+ is already registered/)
end

it 'should allow registering multiple locks with same name and same functions' do
it "should allow registering multiple locks with same name and same functions" do
lock_name = :multiple_locks_with_same_name_test
subject.register_advisory_lock(lock_name, supported_lock_functions[0])

expect { subject.register_advisory_lock(lock_name, supported_lock_functions[0]) }.to_not raise_error
end

it 'registered locks must have different lock keys' do
it "registered locks must have different lock keys" do
quantity = 100
quantity.times do |index|
lock_name = "test_lock_#{index}".to_sym
@@ -74,10 +74,10 @@
expect(all_keys.size).to eq all_keys.uniq.size
end

it 'mapping between lock name and lock key must be constant' do
it "mapping between lock name and lock key must be constant" do
expect(subject.registered_advisory_locks).to be_empty

lock_names_keys_mapping = YAML.load_file(File.join(File.dirname(__FILE__), 'lock_names_keys.yml'))
lock_names_keys_mapping = YAML.load_file(File.join(File.dirname(__FILE__), "lock_names_keys.yml"))

lock_names_keys_mapping.each do |lock_name, valid_lock_key|
lock_name = lock_name.to_sym