Skip to content

Commit 41b9b69

Browse files
Merge pull request #156 from doctolib/Fix_lock_timeout_increase
fix the increase lock timeout on retry
2 parents b0efa6d + abf376a commit 41b9b69

File tree

8 files changed

+53
-40
lines changed

8 files changed

+53
-40
lines changed

.github/workflows/continuous-integration-workflow.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
strategy:
3333
matrix:
3434
postgres: [ 11.7, 12.14, 15.2 ]
35-
ruby: [ 3.0, 3.1, 3.2 ]
35+
ruby: [ 3.1, 3.2 ]
3636
services:
3737
postgres:
3838
image: postgres:${{ matrix.postgres }}

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,4 +99,4 @@ DEPENDENCIES
9999
strong_migrations
100100

101101
BUNDLED WITH
102-
2.2.16
102+
2.6.6

gemfiles/activerecord61.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ gemspec path: '..'
66

77
gem 'activerecord', '~> 6.1.0'
88
gem 'bundler'
9+
gem 'concurrent-ruby', '1.3.4'
910
gem 'debug'
1011
gem 'minitest', '>= 5'
1112
gem 'mocha'

gemfiles/activerecord70.gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ gemspec path: '..'
66

77
gem 'activerecord', '~> 7.0.0'
88
gem 'bundler'
9+
gem 'concurrent-ruby', '1.3.4'
910
gem 'debug'
1011
gem 'minitest', '>= 5'
1112
gem 'mocha'

lib/safe-pg-migrations/configuration.rb

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
# frozen_string_literal: true
22

33
require 'active_support/core_ext/numeric/time'
4+
require 'safe-pg-migrations/helpers/pg_helper'
45

56
module SafePgMigrations
67
class Configuration
8+
include Helpers::PgHelper
9+
710
attr_accessor(*%i[
811
backfill_batch_size
912
backfill_pause
@@ -77,12 +80,5 @@ def pg_lock_timeout
7780
# By reducing the lock timeout by a very small margin, we ensure that the lock timeout is raised in priority
7881
pg_duration safe_timeout * 0.99
7982
end
80-
81-
private
82-
83-
def pg_duration(duration)
84-
value, unit = duration.integer? ? [duration, 's'] : [(duration * 1000).to_i, 'ms']
85-
"#{value}#{unit}"
86-
end
8783
end
8884
end
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# frozen_string_literal: true
2+
3+
module SafePgMigrations
4+
module Helpers
5+
module PgHelper
6+
def pg_duration(duration)
7+
value, unit = duration.integer? ? [duration, 's'] : [(duration * 1000).to_i, 'ms']
8+
"#{value}#{unit}"
9+
end
10+
end
11+
end
12+
end

lib/safe-pg-migrations/plugins/statement_retrier.rb

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
module SafePgMigrations
44
module StatementRetrier
55
include Helpers::StatementsHelper
6+
include Helpers::PgHelper
67

78
RETRIABLE_SCHEMA_STATEMENTS.each do |method|
89
define_method method do |*args, **options, &block|
@@ -13,17 +14,14 @@ module StatementRetrier
1314
private
1415

1516
def retry_if_lock_timeout
16-
initial_lock_timeout = SafePgMigrations.config.lock_timeout
17+
@lock_timeout = SafePgMigrations.config.lock_timeout
1718
number_of_retries = 0
1819
begin
1920
number_of_retries += 1
2021
yield
21-
rescue ActiveRecord::LockWaitTimeout
22+
rescue ActiveRecord::LockWaitTimeout => e
2223
# Retrying is useless if we're inside a transaction.
23-
if transaction_open? || number_of_retries >= SafePgMigrations.config.max_tries
24-
SafePgMigrations.config.lock_timeout = initial_lock_timeout
25-
raise
26-
end
24+
raise e if transaction_open? || number_of_retries >= SafePgMigrations.config.max_tries
2725

2826
retry_delay = SafePgMigrations.config.retry_delay
2927
Helpers::Logger.say "Retrying in #{retry_delay} seconds...", sub_item: true
@@ -39,20 +37,23 @@ def retry_if_lock_timeout
3937
end
4038

4139
def increase_lock_timeout
42-
Helpers::Logger.say " Increasing the lock timeout... Currently set to #{SafePgMigrations.config.lock_timeout}",
40+
Helpers::Logger.say " Increasing the lock timeout... Currently set to #{pg_duration(@lock_timeout)}",
4341
sub_item: true
44-
SafePgMigrations.config.lock_timeout = (SafePgMigrations.config.lock_timeout + lock_timeout_step)
45-
unless SafePgMigrations.config.lock_timeout < SafePgMigrations.config.max_lock_timeout_for_retry
46-
SafePgMigrations.config.lock_timeout = SafePgMigrations.config.max_lock_timeout_for_retry
42+
@lock_timeout += lock_timeout_step
43+
unless @lock_timeout < SafePgMigrations.config.max_lock_timeout_for_retry
44+
@lock_timeout = SafePgMigrations.config.max_lock_timeout_for_retry
4745
end
48-
Helpers::Logger.say " Lock timeout is now set to #{SafePgMigrations.config.lock_timeout}", sub_item: true
46+
execute("SET lock_timeout TO '#{pg_duration(@lock_timeout)}'")
47+
Helpers::Logger.say " Lock timeout is now set to #{pg_duration(@lock_timeout)}", sub_item: true
4948
end
5049

5150
def lock_timeout_step
51+
return @lock_timeout_step if defined?(@lock_timeout_step)
52+
5253
max_lock_timeout_for_retry = SafePgMigrations.config.max_lock_timeout_for_retry
5354
lock_timeout = SafePgMigrations.config.lock_timeout
5455
max_tries = SafePgMigrations.config.max_tries
55-
@lock_timeout_step ||= (max_lock_timeout_for_retry - lock_timeout) / (max_tries - 1)
56+
@lock_timeout_step = (max_lock_timeout_for_retry - lock_timeout) / (max_tries - 1)
5657
end
5758
end
5859
end

test/statement_retrier_test.rb

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,26 +7,28 @@ def test_lock_timeout_increase_on_retry
77
SafePgMigrations.config.lock_timeout = 0.1.seconds
88
SafePgMigrations.config.increase_lock_timeout_on_retry = true
99

10-
calls = calls_for_lock_timeout_migration
10+
2.times do
11+
calls = calls_for_lock_timeout_migration
1112

12-
assert_equal [
13-
' -> Retrying in 60 seconds...',
14-
' -> Increasing the lock timeout... Currently set to 0.1',
15-
' -> Lock timeout is now set to 0.325',
16-
' -> Retrying now.',
17-
' -> Retrying in 60 seconds...',
18-
' -> Increasing the lock timeout... Currently set to 0.325',
19-
' -> Lock timeout is now set to 0.55',
20-
' -> Retrying now.',
21-
' -> Retrying in 60 seconds...',
22-
' -> Increasing the lock timeout... Currently set to 0.55',
23-
' -> Lock timeout is now set to 0.775',
24-
' -> Retrying now.',
25-
' -> Retrying in 60 seconds...',
26-
' -> Increasing the lock timeout... Currently set to 0.775',
27-
' -> Lock timeout is now set to 1',
28-
' -> Retrying now.',
29-
], calls[1..].map(&:first)
13+
assert_equal [
14+
' -> Retrying in 60 seconds...',
15+
' -> Increasing the lock timeout... Currently set to 100ms',
16+
' -> Lock timeout is now set to 325ms',
17+
' -> Retrying now.',
18+
' -> Retrying in 60 seconds...',
19+
' -> Increasing the lock timeout... Currently set to 325ms',
20+
' -> Lock timeout is now set to 550ms',
21+
' -> Retrying now.',
22+
' -> Retrying in 60 seconds...',
23+
' -> Increasing the lock timeout... Currently set to 550ms',
24+
' -> Lock timeout is now set to 775ms',
25+
' -> Retrying now.',
26+
' -> Retrying in 60 seconds...',
27+
' -> Increasing the lock timeout... Currently set to 775ms',
28+
' -> Lock timeout is now set to 1s',
29+
' -> Retrying now.',
30+
], calls[1..].map(&:first)
31+
end
3032
end
3133

3234
def test_no_lock_timeout_increase_on_retry_if_disabled

0 commit comments

Comments
 (0)