online_migrations 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/docs/background_data_migrations.md +10 -0
- data/docs/background_schema_migrations.md +10 -0
- data/lib/online_migrations/background_migrations/scheduler.rb +12 -14
- data/lib/online_migrations/background_schema_migrations/scheduler.rb +16 -14
- data/lib/online_migrations/version.rb +1 -1
- data/lib/online_migrations.rb +12 -5
- metadata +2 -3
- data/lib/online_migrations/advisory_lock.rb +0 -60
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99170e244f0f008e8d097222c88a96555692aa5afc136087d03341866ed8412c
|
4
|
+
data.tar.gz: 6068a722274c73d8a0ab3c1bed4d1f2673cd99d15ca77c24f7fff5af75e77129
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d2a127aee978d36676d8bdd85352487f66a14289cca28173eb1126aee65ceeea5dda067171ffb320b7c0cb2be903712ab158af12312452180b8e56da1e7b641
|
7
|
+
data.tar.gz: 305f89162f87a3746d91bfdfacae00b7d0f2a374f27755c846cd4386d65694442b70fc6963a7dec8845c192eca2cc625bb020c7c4aec664320a395b124bb3e71
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,15 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
+
## 0.24.0 (2025-01-20)
|
4
|
+
|
5
|
+
- Add ability to run a separate background migrations scheduler per shard
|
6
|
+
|
7
|
+
- Revert "Prevent multiple instances of schedulers from being running simultaneously"
|
8
|
+
|
9
|
+
The feature was implemented using advisory locks, but they do not always play nicely with the
|
10
|
+
database poolers, so the feature was reverted. It is expected now for user's code to prevent
|
11
|
+
multiple instances of the schedulers from being run (by wrapping it into Redis lock etc).
|
12
|
+
|
3
13
|
## 0.23.0 (2025-01-13)
|
4
14
|
|
5
15
|
- Prevent multiple instances of schedulers from being running simultaneously
|
@@ -349,3 +349,13 @@ OnlineMigrations::ApplicationRecord.connects_to database: { writing: :shard_one
|
|
349
349
|
|
350
350
|
By default, ActiveRecord uses the database config named `:primary` (if exists) under the environment section from the `database.yml`.
|
351
351
|
Otherwise, the first config under the environment section is used.
|
352
|
+
|
353
|
+
By default, the scheduler works on a single shard on each run. To run a separate scheduler per shard:
|
354
|
+
|
355
|
+
```ruby
|
356
|
+
[:shard_one, :shard_two, :shard_three].each do |shard|
|
357
|
+
every 1.minute do
|
358
|
+
runner "OnlineMigrations.run_background_data_migrations(shard: :#{shard})"
|
359
|
+
end
|
360
|
+
end
|
361
|
+
```
|
@@ -192,3 +192,13 @@ OnlineMigrations::ApplicationRecord.connects_to database: { writing: :shard_one
|
|
192
192
|
|
193
193
|
By default, ActiveRecord uses the database config named `:primary` (if exists) under the environment section from the `database.yml`.
|
194
194
|
Otherwise, the first config under the environment section is used.
|
195
|
+
|
196
|
+
By default, the scheduler works on a single shard on each run. To run a separate scheduler per shard:
|
197
|
+
|
198
|
+
```ruby
|
199
|
+
[:shard_one, :shard_two, :shard_three].each do |shard|
|
200
|
+
every 1.minute do
|
201
|
+
runner "OnlineMigrations.run_background_schema_migrations(shard: :#{shard})"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
```
|
@@ -9,36 +9,34 @@ module OnlineMigrations
|
|
9
9
|
# successive runs has passed.
|
10
10
|
#
|
11
11
|
# Scheduler should be configured to run periodically, for example, via cron.
|
12
|
+
#
|
12
13
|
# @example Run via whenever
|
13
14
|
# # add this to schedule.rb
|
14
15
|
# every 1.minute do
|
15
|
-
# runner "OnlineMigrations.
|
16
|
+
# runner "OnlineMigrations.run_background_data_migrations"
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# @example Run via whenever (specific shard)
|
20
|
+
# every 1.minute do
|
21
|
+
# runner "OnlineMigrations.run_background_data_migrations(shard: :shard_two)"
|
16
22
|
# end
|
17
23
|
#
|
18
24
|
class Scheduler
|
19
|
-
def self.run
|
20
|
-
new.run
|
25
|
+
def self.run(**options)
|
26
|
+
new.run(**options)
|
21
27
|
end
|
22
28
|
|
23
29
|
# Runs Scheduler
|
24
|
-
def run
|
30
|
+
def run(**options)
|
25
31
|
active_migrations = Migration.runnable.active.queue_order
|
32
|
+
active_migrations = active_migrations.where(shard: options[:shard]) if options.key?(:shard)
|
26
33
|
runnable_migration = active_migrations.select(&:interval_elapsed?).first
|
27
34
|
|
28
35
|
if runnable_migration
|
29
36
|
runner = MigrationRunner.new(runnable_migration)
|
30
|
-
|
31
|
-
try_with_lock do
|
32
|
-
runner.run_migration_job
|
33
|
-
end
|
37
|
+
runner.run_migration_job
|
34
38
|
end
|
35
39
|
end
|
36
|
-
|
37
|
-
private
|
38
|
-
def try_with_lock(&block)
|
39
|
-
lock = AdvisoryLock.new(name: "online_migrations_data_scheduler")
|
40
|
-
lock.try_with_lock(&block)
|
41
|
-
end
|
42
40
|
end
|
43
41
|
end
|
44
42
|
end
|
@@ -7,34 +7,41 @@ module OnlineMigrations
|
|
7
7
|
# running migration on the same table.
|
8
8
|
#
|
9
9
|
# Scheduler should be configured to run periodically, for example, via cron.
|
10
|
+
#
|
10
11
|
# @example Run via whenever
|
11
12
|
# # add this to schedule.rb
|
12
13
|
# every 1.minute do
|
13
14
|
# runner "OnlineMigrations.run_background_schema_migrations"
|
14
15
|
# end
|
15
16
|
#
|
17
|
+
# @example Run via whenever (specific shard)
|
18
|
+
# every 1.minute do
|
19
|
+
# runner "OnlineMigrations.run_background_schema_migrations(shard: :shard_two)"
|
20
|
+
# end
|
21
|
+
#
|
16
22
|
class Scheduler
|
17
|
-
def self.run
|
18
|
-
new.run
|
23
|
+
def self.run(**options)
|
24
|
+
new.run(**options)
|
19
25
|
end
|
20
26
|
|
21
27
|
# Runs Scheduler
|
22
|
-
def run
|
23
|
-
migration = find_migration
|
28
|
+
def run(**options)
|
29
|
+
migration = find_migration(**options)
|
24
30
|
if migration
|
25
31
|
runner = MigrationRunner.new(migration)
|
26
|
-
|
27
|
-
try_with_lock do
|
28
|
-
runner.run
|
29
|
-
end
|
32
|
+
runner.run
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
36
|
private
|
34
|
-
def find_migration
|
37
|
+
def find_migration(**options)
|
35
38
|
active_migrations = Migration.running.reject(&:stuck?)
|
36
39
|
runnable_migrations = Migration.runnable.enqueued.queue_order.to_a + Migration.retriable.queue_order.to_a
|
37
40
|
|
41
|
+
if options.key?(:shard)
|
42
|
+
runnable_migrations = runnable_migrations.select { |migration| migration.shard.to_s == options[:shard].to_s }
|
43
|
+
end
|
44
|
+
|
38
45
|
runnable_migrations.find do |runnable_migration|
|
39
46
|
active_migrations.none? do |active_migration|
|
40
47
|
active_migration.connection_class_name == runnable_migration.connection_class_name &&
|
@@ -43,11 +50,6 @@ module OnlineMigrations
|
|
43
50
|
end
|
44
51
|
end
|
45
52
|
end
|
46
|
-
|
47
|
-
def try_with_lock(&block)
|
48
|
-
lock = AdvisoryLock.new(name: "online_migrations_schema_scheduler")
|
49
|
-
lock.try_with_lock(&block)
|
50
|
-
end
|
51
53
|
end
|
52
54
|
end
|
53
55
|
end
|
data/lib/online_migrations.rb
CHANGED
@@ -23,7 +23,6 @@ module OnlineMigrations
|
|
23
23
|
|
24
24
|
extend ActiveSupport::Autoload
|
25
25
|
|
26
|
-
autoload :AdvisoryLock
|
27
26
|
autoload :ApplicationRecord
|
28
27
|
autoload :BatchIterator
|
29
28
|
autoload :VerboseSqlLogs
|
@@ -84,14 +83,22 @@ module OnlineMigrations
|
|
84
83
|
end
|
85
84
|
|
86
85
|
# Run background data migrations
|
87
|
-
|
88
|
-
|
86
|
+
#
|
87
|
+
# @option options [String, Symbol, nil] :shard The name of the shard to run
|
88
|
+
# background data migrations on. By default runs on all shards.
|
89
|
+
#
|
90
|
+
def run_background_migrations(**options)
|
91
|
+
BackgroundMigrations::Scheduler.run(**options)
|
89
92
|
end
|
90
93
|
alias run_background_data_migrations run_background_migrations
|
91
94
|
|
92
95
|
# Run background schema migrations
|
93
|
-
|
94
|
-
|
96
|
+
#
|
97
|
+
# @option options [String, Symbol, nil] :shard The name of the shard to run
|
98
|
+
# background schema migrations on. By default runs on all shards.
|
99
|
+
#
|
100
|
+
def run_background_schema_migrations(**options)
|
101
|
+
BackgroundSchemaMigrations::Scheduler.run(**options)
|
95
102
|
end
|
96
103
|
|
97
104
|
def deprecator
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: online_migrations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.24.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- fatkodima
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-01-
|
11
|
+
date: 2025-01-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -48,7 +48,6 @@ files:
|
|
48
48
|
- lib/generators/online_migrations/templates/migration.rb.tt
|
49
49
|
- lib/generators/online_migrations/upgrade_generator.rb
|
50
50
|
- lib/online_migrations.rb
|
51
|
-
- lib/online_migrations/advisory_lock.rb
|
52
51
|
- lib/online_migrations/application_record.rb
|
53
52
|
- lib/online_migrations/background_migration.rb
|
54
53
|
- lib/online_migrations/background_migrations/backfill_column.rb
|
@@ -1,60 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "zlib"
|
4
|
-
|
5
|
-
module OnlineMigrations
|
6
|
-
# @private
|
7
|
-
class AdvisoryLock
|
8
|
-
attr_reader :name, :connection
|
9
|
-
|
10
|
-
def initialize(name:, connection: ApplicationRecord.connection)
|
11
|
-
@name = name
|
12
|
-
@connection = connection
|
13
|
-
end
|
14
|
-
|
15
|
-
def try_lock
|
16
|
-
locked = connection.select_value("SELECT pg_try_advisory_lock(#{lock_key})")
|
17
|
-
Utils.to_bool(locked)
|
18
|
-
end
|
19
|
-
|
20
|
-
def unlock
|
21
|
-
connection.select_value("SELECT pg_advisory_unlock(#{lock_key})")
|
22
|
-
end
|
23
|
-
|
24
|
-
# Runs the given block if an advisory lock is able to be acquired.
|
25
|
-
def try_with_lock
|
26
|
-
if try_lock
|
27
|
-
begin
|
28
|
-
yield
|
29
|
-
ensure
|
30
|
-
unlock
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def active?
|
36
|
-
objid = lock_key & 0xffffffff
|
37
|
-
classid = lock_key >> 32
|
38
|
-
|
39
|
-
active = connection.select_value(<<~SQL)
|
40
|
-
SELECT granted
|
41
|
-
FROM pg_locks
|
42
|
-
WHERE locktype = 'advisory'
|
43
|
-
AND pid = pg_backend_pid()
|
44
|
-
AND mode = 'ExclusiveLock'
|
45
|
-
AND classid = #{classid}
|
46
|
-
AND objid = #{objid}
|
47
|
-
SQL
|
48
|
-
|
49
|
-
Utils.to_bool(active)
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
SALT = 936723412
|
54
|
-
|
55
|
-
def lock_key
|
56
|
-
name_hash = Zlib.crc32(name)
|
57
|
-
SALT * name_hash
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|