Skip to content

Commit

Permalink
MySQL specific gaplock protection is configurable
Browse files Browse the repository at this point in the history
Instead of depending on the name of the ActiveRecord adapter to identify if the database is MySQL, gaplock protection is configurable, as suggested by @jurre on #399
  • Loading branch information
thom-oman committed Apr 29, 2020
1 parent eedc7a7 commit 5167c2c
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 8 deletions.
5 changes: 5 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ jobs:
- RAILS_VERSION=5.2.4
- DATABASE_URL=mysql2://[email protected]/statesman_test
- DATABASE_DEPENDENCY_PORT=3306
- GAPLOCK_PROTECTION=true
- image: circleci/mysql:5.7.18
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=true
Expand Down Expand Up @@ -66,6 +67,7 @@ jobs:
- RAILS_VERSION=6.0.2
- DATABASE_URL=mysql2://[email protected]/statesman_test
- DATABASE_DEPENDENCY_PORT=3306
- GAPLOCK_PROTECTION=true
- image: circleci/mysql:5.7.18
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=true
Expand Down Expand Up @@ -93,6 +95,7 @@ jobs:
- RAILS_VERSION=master
- DATABASE_URL=mysql2://[email protected]/statesman_test
- DATABASE_DEPENDENCY_PORT=3306
- GAPLOCK_PROTECTION=true
- image: circleci/mysql:5.7.18
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=true
Expand Down Expand Up @@ -122,6 +125,7 @@ jobs:
- RAILS_VERSION=6.0.2
- DATABASE_URL=mysql2://[email protected]/statesman_test
- DATABASE_DEPENDENCY_PORT=3306
- GAPLOCK_PROTECTION=true
- image: circleci/mysql:5.7.18
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=true
Expand Down Expand Up @@ -149,6 +153,7 @@ jobs:
- RAILS_VERSION=master
- DATABASE_URL=mysql2://[email protected]/statesman_test
- DATABASE_DEPENDENCY_PORT=3306
- GAPLOCK_PROTECTION=true
- image: circleci/mysql:5.7.18
environment:
- MYSQL_ALLOW_EMPTY_PASSWORD=true
Expand Down
13 changes: 10 additions & 3 deletions lib/statesman.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,18 @@ module Adapters
# end
#
def self.configure(&block)
config = Config.new(block)
@storage_adapter = config.adapter_class
@config = Config.new(block)
end

def self.storage_adapter
@storage_adapter || Adapters::Memory
config.adapter_class || Adapters::Memory
end

def self.gaplock_protection_enabled?
!!config.gaplock_protection_enabled
end

def self.config
@config
end
end
8 changes: 4 additions & 4 deletions lib/statesman/adapters/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ def create_transition(from, to, metadata)
::ActiveRecord::Base.transaction(requires_new: true) do
@observer.execute(:before, from, to, transition)

if db_mysql?
if gaplock_protection_enabled?
# We save the transition first with most_recent falsy, then mark most_recent
# true after to avoid letting MySQL acquire a next-key lock which can cause
# deadlocks.
Expand Down Expand Up @@ -136,7 +136,7 @@ def update_most_recents(most_recent_id = nil)
# MySQL will validate index constraints across the intermediate result of an
# update. This means we must order our update to deactivate the previous
# most_recent before setting the new row to be true.
update.order(transition_table[:most_recent].desc) if db_mysql?
update.order(transition_table[:most_recent].desc) if gaplock_protection_enabled?

::ActiveRecord::Base.connection.update(update.to_sql)
end
Expand Down Expand Up @@ -292,8 +292,8 @@ def updated_column_and_timestamp
]
end

def db_mysql?
::ActiveRecord::Base.connection.adapter_name.downcase.starts_with?("mysql")
def gaplock_protection_enabled?
Statesman.gaplock_protection_enabled?
end

def db_true
Expand Down
7 changes: 6 additions & 1 deletion lib/statesman/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@

module Statesman
class Config
attr_reader :adapter_class
attr_reader :adapter_class, :gaplock_protection_enabled

def initialize(block = nil)
@gaplock_protection_enabled = false
instance_eval(&block) unless block.nil?
end

def storage_adapter(adapter_class)
@adapter_class = adapter_class
end

def mysql_gaplock_protection(gaplock_protection)
@gaplock_protection_enabled = gaplock_protection
end
end
end
5 changes: 5 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@
require "rspec/its"
require "pry"

Statesman.configure do
# These ENV vars are only set on the MySQL builds
mysql_gaplock_protection ENV.has_key?("GAPLOCK_PROTECTION")
end

RSpec.configure do |config|
config.raise_errors_for_deprecations!
config.mock_with(:rspec) { |mocks| mocks.verify_partial_doubles = true }
Expand Down

0 comments on commit 5167c2c

Please sign in to comment.