Use a journalized-scope advisory lock to minimize exclusive locks

This commit removes transactional_lock (mysql-support only) with
`with_advisory_lock` which supports both MySQL and PostgresSQL.

For PostgreSQL, which supports more than one advisory lock per session,
we can reference the customized type and ID to minimize the locking we
do for other work packages.
pull/7244/head
Oliver Günther 6 years ago
parent ccbe76d887
commit e4ccc964b3
No known key found for this signature in database
GPG Key ID: A3A8BDAD7C0C552C
  1. 3
      Gemfile
  2. 12
      Gemfile.lock
  3. 20
      app/models/journal.rb
  4. 3
      app/models/journal_manager.rb
  5. 36
      config/initializers/transactional_lock.rb

@ -138,8 +138,7 @@ gem 'lograge', '~> 0.10.0'
# don't require by default, instead load on-demand when actually configured
gem 'airbrake', '~> 8.0.1', require: false
gem 'transactional_lock', git: 'https://github.com/finnlabs/transactional_lock.git',
branch: 'master'
gem 'with_advisory_lock'
gem 'prawn', '~> 2.2'
gem 'prawn-table', '~> 0.2.2'

@ -38,14 +38,6 @@ GIT
specs:
rspec-example_disabler (0.0.1)
GIT
remote: https://github.com/finnlabs/transactional_lock.git
revision: 6948b1d446db0da5645e68ffeeddca1c4944c3bc
branch: master
specs:
transactional_lock (0.1.0)
activerecord (>= 4.0)
GIT
remote: https://github.com/goodwill/capybara-select2
revision: 585192e4bb0db8d52e761ab68f08c17294806447
@ -898,6 +890,8 @@ GEM
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
will_paginate (3.1.6)
with_advisory_lock (4.0.0)
activerecord (>= 4.2)
xpath (3.2.0)
nokogiri (~> 1.8)
@ -1055,7 +1049,6 @@ DEPENDENCIES
test-prof (~> 0.7.3)
thin (~> 1.7.2)
timecop (~> 0.9.0)
transactional_lock!
typed_dag (~> 2.0.2)
tzinfo-data (~> 1.2018.9)
unicorn
@ -1064,6 +1057,7 @@ DEPENDENCIES
warden-basic_auth (~> 0.2.1)
webmock (~> 3.5.0)
will_paginate (~> 3.1.0)
with_advisory_lock
RUBY VERSION
ruby 2.6.1p33

@ -59,19 +59,17 @@ class Journal < ActiveRecord::Base
# Note for PostgreSQL: If this is called from inside a transaction, the lock will last until the
# end of that transaction.
# Note for MySQL: THis method does not currently change anything (no locking at all)
def self.with_write_lock
def self.with_write_lock(journable)
lock_name =
if OpenProject::Database.mysql?
Journal.transaction do
# MySQL is very weak when combining transactions and locks. Using an emulation layer to
# automatically release an advisory lock at the end of the transaction
TransactionalLock::AdvisoryLock.new('journals.write_lock').acquire
yield
end
# MySQL only supports a single lock
"journals.write_lock"
else
Journal.transaction do
ActiveRecord::Base.connection.execute("LOCK TABLE #{table_name} IN SHARE ROW EXCLUSIVE MODE")
yield
end
"journal.#{journable.class}.#{journable.id}"
end
Journal.with_advisory_lock(lock_name, timeout_seconds: 60) do
yield
end
end

@ -227,7 +227,8 @@ class JournalManager
def self.add_journal!(journable, user = User.current, notes = '')
if journalized? journable
# Obtain a table lock to ensure consistent version numbers
Journal.with_write_lock do
Journal.with_write_lock(journable) do
# Maximum version might be nil, so use to_i here.
version = journable.journals.maximum(:version).to_i + 1

@ -1,36 +0,0 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2018 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2017 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
OpenProject::Application.configure do
config.after_initialize do
TransactionalLock.initialize do |config|
config.merge(default_timeout: 60)
end
end
end
Loading…
Cancel
Save