Use an advisory lock that is released on COMMIT/ROLLBACK

for MySQL this seems to be the only available workaround that is not destroying running transactions.

Note that TransactionalLock is a self-developed gem, that will emulate PostgreSQL style advisory locks, that are automatically released at transaction end.
pull/3482/head
Jan Sandbrink 9 years ago
parent 3c335866d1
commit 9d68773b05
  1. 2
      Gemfile
  2. 8
      Gemfile.lock
  3. 13
      app/models/journal.rb
  4. 34
      config/initializers/transactional_lock.rb

@ -99,6 +99,8 @@ gem 'gon', '~> 4.0'
# don't require by default, instead load on-demand when actually configured
gem 'airbrake', '~> 4.1.0', require: false
gem 'transactional_lock', git: 'https://github.com/finnlabs/transactional_lock.git', branch: 'master'
group :production do
# we use dalli as standard memcache client
# requires memcached 1.4+

@ -28,6 +28,13 @@ GIT
specs:
rspec-example_disabler (0.0.1)
GIT
remote: https://github.com/finnlabs/transactional_lock.git
revision: fe82192efa0346e49d19bbe82865280fdae79487
branch: master
specs:
transactional_lock (0.1.0)
GIT
remote: https://github.com/opf/openproject-translations.git
revision: a2a7d246c6212c0867970ee44c505f97e5c96ee3
@ -596,6 +603,7 @@ DEPENDENCIES
sys-filesystem (~> 1.1.4)
thin
timecop (~> 0.7.1)
transactional_lock!
unicorn
warden (~> 1.2)
warden-basic_auth (~> 0.2.1)

@ -63,11 +63,14 @@ class Journal < ActiveRecord::Base
# Note for MySQL: THis method does not currently change anything (no locking at all)
def self.with_write_lock
if OpenProject::Database.mysql?
# N.B. there seems to be no acceptable locking available for MySQL:
# - table locks will break (i.e. COMMIT) an already running transaction.
# - advisory locks will do their job, except that when they end inside a transaction,
# nobody will see the changes from inside the lock until the transaction finished.
yield
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
# FIXME: still creates duplicates... because MySQL defaults to READ REPEATABLE isolation
# we need READ COMMITED, which is also the default for PostgreSQL
TransactionalLock::AdvisoryLock.new('journals.write_lock').acquire
yield
end
else
Journal.transaction do
ActiveRecord::Base.connection.execute("LOCK TABLE #{table_name} IN SHARE ROW EXCLUSIVE MODE")

@ -0,0 +1,34 @@
#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 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-2013 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 doc/COPYRIGHT.rdoc for more details.
#++
OpenProject::Application.configure do
config.after_initialize do
TransactionalLock.initialize
end
end
Loading…
Cancel
Save