online_migrations 0.22.0 → 0.23.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 +12 -0
- data/README.md +4 -4
- data/lib/generators/online_migrations/templates/initializer.rb.tt +17 -15
- data/lib/online_migrations/advisory_lock.rb +60 -0
- data/lib/online_migrations/background_migrations/config.rb +4 -36
- data/lib/online_migrations/background_migrations/migration.rb +1 -6
- data/lib/online_migrations/background_migrations/migration_helpers.rb +2 -2
- data/lib/online_migrations/background_migrations/migration_job.rb +1 -6
- data/lib/online_migrations/background_migrations/scheduler.rb +10 -1
- data/lib/online_migrations/background_schema_migrations/migration.rb +1 -6
- data/lib/online_migrations/background_schema_migrations/scheduler.rb +9 -1
- data/lib/online_migrations/change_column_type_helpers.rb +12 -40
- data/lib/online_migrations/command_checker.rb +26 -79
- data/lib/online_migrations/error_messages.rb +6 -15
- data/lib/online_migrations/schema_statements.rb +23 -99
- data/lib/online_migrations/utils.rb +0 -20
- data/lib/online_migrations/verbose_sql_logs.rb +3 -20
- data/lib/online_migrations/version.rb +1 -1
- data/lib/online_migrations.rb +1 -1
- metadata +6 -6
- data/lib/online_migrations/indexes_collector.rb +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1052d2cc58898bc561b5e755daccc137d7d6e1d35afa3af5968c2b655ebe5805
|
4
|
+
data.tar.gz: ad72d601c36ea9192f192fb8941d30a2cfbcf0ed87e0717fb5d4ebf47b20f169
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 697ac61bcb41e00744cfa87965910a3e1d68eaa571ad5c6dc3b3d8a4d14deb16ae0cbc0d9771d8d57668be8529f200736a14c1769d7d8cf6c64fc083060ff74c
|
7
|
+
data.tar.gz: 7b4e383f7628ba5cc74c2bb86f4460890b70f3052738f0f20db3571ee770e22c49bc548f5c0ef3fd47caa456a84eb4174f031742869612ee3b27505faf49ecec
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
## master (unreleased)
|
2
2
|
|
3
|
+
## 0.23.0 (2025-01-13)
|
4
|
+
|
5
|
+
- Prevent multiple instances of schedulers from being running simultaneously
|
6
|
+
|
7
|
+
- Reduce default batch sizes for background data migrations
|
8
|
+
|
9
|
+
`batch_size` was 20_000, now 1_000; `sub_batch_size` was 1_000, now 100
|
10
|
+
|
11
|
+
- Remove deprecated code
|
12
|
+
- Drop support for PostgreSQL < 12
|
13
|
+
- Drop support for Ruby < 3.0 and Rails < 7.0
|
14
|
+
|
3
15
|
## 0.22.0 (2025-01-03)
|
4
16
|
|
5
17
|
- Make background data migrations scheduler run a single migration at a time
|
data/README.md
CHANGED
@@ -16,11 +16,11 @@ See [comparison to `strong_migrations`](#comparison-to-strong_migrations)
|
|
16
16
|
|
17
17
|
## Requirements
|
18
18
|
|
19
|
-
- Ruby
|
20
|
-
- Rails
|
21
|
-
- PostgreSQL
|
19
|
+
- Ruby 3.0+
|
20
|
+
- Rails 7.0+
|
21
|
+
- PostgreSQL 12+
|
22
22
|
|
23
|
-
For older Ruby and Rails versions you can use
|
23
|
+
For older Ruby and Rails versions you can use older versions of this gem.
|
24
24
|
|
25
25
|
**Note**: Since some migration helpers use database `VIEW`s to implement their logic, it is recommended to use `structure.sql` schema format, or otherwise add some gem (like [scenic](https://github.com/scenic-views/scenic)) to be able to dump them into the `schema.rb`.
|
26
26
|
|
@@ -4,11 +4,13 @@ OnlineMigrations.configure do |config|
|
|
4
4
|
# Configure the migration version starting after which checks are performed.
|
5
5
|
# config.start_after = <%= start_after %>
|
6
6
|
|
7
|
-
# Configure statement timeout
|
7
|
+
# Configure statement timeout for migrations (in seconds).
|
8
|
+
# Note: Background data migrations use application specific timeouts and
|
9
|
+
# background schema migrations use their custom timeouts.
|
8
10
|
config.statement_timeout = 1.hour
|
9
11
|
|
10
12
|
# Set the version of the production database so the right checks are run in development.
|
11
|
-
# config.target_version =
|
13
|
+
# config.target_version = 17
|
12
14
|
|
13
15
|
# Configure whether to perform checks when migrating down.
|
14
16
|
config.check_down = false
|
@@ -19,7 +21,7 @@ OnlineMigrations.configure do |config|
|
|
19
21
|
|
20
22
|
# Maximum allowed lock timeout value (in seconds).
|
21
23
|
# If set lock timeout is greater than this value, the migration will fail.
|
22
|
-
|
24
|
+
config.lock_timeout_limit = 10.seconds
|
23
25
|
|
24
26
|
# Configure list of tables with permanently small number of records.
|
25
27
|
# This tables are usually tables like "settings", "prices", "plans" etc.
|
@@ -28,10 +30,10 @@ OnlineMigrations.configure do |config|
|
|
28
30
|
|
29
31
|
# Analyze tables after indexes are added.
|
30
32
|
# Outdated statistics can sometimes hurt performance.
|
31
|
-
|
33
|
+
config.auto_analyze = false
|
32
34
|
|
33
35
|
# Alphabetize table columns when dumping the schema.
|
34
|
-
|
36
|
+
config.alphabetize_schema = false
|
35
37
|
|
36
38
|
# Disable specific checks.
|
37
39
|
# For the list of available checks look at the `error_messages.rb` file inside
|
@@ -92,29 +94,29 @@ OnlineMigrations.configure do |config|
|
|
92
94
|
|
93
95
|
# ==> Background data migrations configuration
|
94
96
|
# The path where generated background migrations will be placed.
|
95
|
-
|
97
|
+
config.background_migrations.migrations_path = "lib"
|
96
98
|
|
97
99
|
# The module in which background migrations will be placed.
|
98
|
-
|
100
|
+
config.background_migrations.migrations_module = "OnlineMigrations::BackgroundMigrations"
|
99
101
|
|
100
102
|
# The number of rows to process in a single background migration run.
|
101
|
-
|
103
|
+
config.background_migrations.batch_size = 1_000
|
102
104
|
|
103
105
|
# The smaller batches size that the batches will be divided into.
|
104
|
-
|
106
|
+
config.background_migrations.sub_batch_size = 100
|
105
107
|
|
106
108
|
# The pause interval between each background migration job's execution (in seconds).
|
107
|
-
|
109
|
+
config.background_migrations.batch_pause = 0.seconds
|
108
110
|
|
109
111
|
# The number of milliseconds to sleep between each sub_batch execution.
|
110
|
-
|
112
|
+
config.background_migrations.sub_batch_pause_ms = 100
|
111
113
|
|
112
114
|
# Maximum number of batch run attempts.
|
113
115
|
# When attempts are exhausted, the individual batch is marked as failed.
|
114
|
-
|
116
|
+
config.background_migrations.batch_max_attempts = 5
|
115
117
|
|
116
118
|
# The number of seconds that must pass before the running job is considered stuck.
|
117
|
-
|
119
|
+
config.background_migrations.stuck_jobs_timeout = 1.hour
|
118
120
|
|
119
121
|
# The callback to perform when an error occurs in the migration job.
|
120
122
|
# config.background_migrations.error_handler = ->(error, errored_job) do
|
@@ -125,10 +127,10 @@ OnlineMigrations.configure do |config|
|
|
125
127
|
|
126
128
|
# ==> Background schema migrations configuration
|
127
129
|
# When attempts are exhausted, the failing migration stops to be retried.
|
128
|
-
|
130
|
+
config.background_schema_migrations.max_attempts = 5
|
129
131
|
|
130
132
|
# Statement timeout value used when running background schema migration.
|
131
|
-
|
133
|
+
config.background_schema_migrations.statement_timeout = 1.hour
|
132
134
|
|
133
135
|
# The callback to perform when an error occurs during the background schema migration.
|
134
136
|
# config.background_schema_migrations.error_handler = ->(error, errored_migration) do
|
@@ -0,0 +1,60 @@
|
|
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
|
@@ -13,12 +13,12 @@ module OnlineMigrations
|
|
13
13
|
attr_accessor :migrations_module
|
14
14
|
|
15
15
|
# The number of rows to process in a single background migration run
|
16
|
-
# @return [Integer] defaults to
|
16
|
+
# @return [Integer] defaults to 1_000
|
17
17
|
#
|
18
18
|
attr_accessor :batch_size
|
19
19
|
|
20
20
|
# The smaller batches size that the batches will be divided into
|
21
|
-
# @return [Integer] defaults to
|
21
|
+
# @return [Integer] defaults to 100
|
22
22
|
#
|
23
23
|
attr_accessor :sub_batch_size
|
24
24
|
|
@@ -39,36 +39,12 @@ module OnlineMigrations
|
|
39
39
|
#
|
40
40
|
attr_accessor :batch_max_attempts
|
41
41
|
|
42
|
-
def throttler
|
43
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
44
|
-
`config.background_migrations.throttler` is deprecated and will be removed.
|
45
|
-
Use `config.throttler` instead.
|
46
|
-
MSG
|
47
|
-
OnlineMigrations.config.throttler
|
48
|
-
end
|
49
|
-
|
50
42
|
# The number of seconds that must pass before the running job is considered stuck
|
51
43
|
#
|
52
44
|
# @return [Integer] defaults to 1 hour
|
53
45
|
#
|
54
46
|
attr_accessor :stuck_jobs_timeout
|
55
47
|
|
56
|
-
def backtrace_cleaner
|
57
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
58
|
-
`config.background_migrations.backtrace_cleaner` is deprecated and will be removed.
|
59
|
-
Use `config.backtrace_cleaner` instead.
|
60
|
-
MSG
|
61
|
-
OnlineMigrations.config.backtrace_cleaner
|
62
|
-
end
|
63
|
-
|
64
|
-
def backtrace_cleaner=(value)
|
65
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
66
|
-
`config.background_migrations.backtrace_cleaner=` is deprecated and will be removed.
|
67
|
-
Use `config.backtrace_cleaner=` instead.
|
68
|
-
MSG
|
69
|
-
OnlineMigrations.config.backtrace_cleaner = value
|
70
|
-
end
|
71
|
-
|
72
48
|
# The callback to perform when an error occurs in the migration job.
|
73
49
|
#
|
74
50
|
# @example
|
@@ -85,22 +61,14 @@ module OnlineMigrations
|
|
85
61
|
def initialize
|
86
62
|
@migrations_path = "lib"
|
87
63
|
@migrations_module = "OnlineMigrations::BackgroundMigrations"
|
88
|
-
@batch_size =
|
89
|
-
@sub_batch_size =
|
64
|
+
@batch_size = 1_000
|
65
|
+
@sub_batch_size = 100
|
90
66
|
@batch_pause = 0.seconds
|
91
67
|
@sub_batch_pause_ms = 100
|
92
68
|
@batch_max_attempts = 5
|
93
69
|
@stuck_jobs_timeout = 1.hour
|
94
70
|
@error_handler = ->(error, errored_job) {}
|
95
71
|
end
|
96
|
-
|
97
|
-
def throttler=(value)
|
98
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
99
|
-
`config.background_migrations.throttler=` is deprecated and will be removed.
|
100
|
-
Use `config.throttler=` instead.
|
101
|
-
MSG
|
102
|
-
OnlineMigrations.config.throttler = value
|
103
|
-
end
|
104
72
|
end
|
105
73
|
end
|
106
74
|
end
|
@@ -32,12 +32,7 @@ module OnlineMigrations
|
|
32
32
|
|
33
33
|
alias_attribute :name, :migration_name
|
34
34
|
|
35
|
-
|
36
|
-
if Utils.ar_version >= 7
|
37
|
-
enum :status, STATUSES.index_with(&:to_s)
|
38
|
-
else
|
39
|
-
enum status: STATUSES.index_with(&:to_s)
|
40
|
-
end
|
35
|
+
enum :status, STATUSES.index_with(&:to_s)
|
41
36
|
|
42
37
|
belongs_to :parent, class_name: name, optional: true, inverse_of: :children
|
43
38
|
has_many :children, class_name: name, foreign_key: :parent_id, dependent: :delete_all, inverse_of: :parent
|
@@ -335,8 +335,8 @@ module OnlineMigrations
|
|
335
335
|
# defaults to `SELECT MIN(batch_column_name)`
|
336
336
|
# @option options [Integer] :max_value Value in the column the batching will end at,
|
337
337
|
# defaults to `SELECT MAX(batch_column_name)`
|
338
|
-
# @option options [Integer] :batch_size (
|
339
|
-
# @option options [Integer] :sub_batch_size (
|
338
|
+
# @option options [Integer] :batch_size (1_000) Number of rows to process in a single background migration run
|
339
|
+
# @option options [Integer] :sub_batch_size (100) Smaller batches size that the batches will be divided into
|
340
340
|
# @option options [Integer] :batch_pause (0) Pause interval between each background migration job's execution (in seconds)
|
341
341
|
# @option options [Integer] :sub_batch_pause_ms (100) Number of milliseconds to sleep between each sub_batch execution
|
342
342
|
# @option options [Integer] :batch_max_attempts (5) Maximum number of batch run attempts
|
@@ -38,12 +38,7 @@ module OnlineMigrations
|
|
38
38
|
scope :except_succeeded, -> { where.not(status: :succeeded) }
|
39
39
|
scope :attempts_exceeded, -> { where("attempts >= max_attempts") }
|
40
40
|
|
41
|
-
|
42
|
-
if Utils.ar_version >= 7
|
43
|
-
enum :status, STATUSES.index_with(&:to_s)
|
44
|
-
else
|
45
|
-
enum status: STATUSES.index_with(&:to_s)
|
46
|
-
end
|
41
|
+
enum :status, STATUSES.index_with(&:to_s)
|
47
42
|
|
48
43
|
delegate :migration_name, :migration_class, :migration_object, :migration_relation, :batch_column_name,
|
49
44
|
:arguments, :batch_pause, to: :migration
|
@@ -27,9 +27,18 @@ module OnlineMigrations
|
|
27
27
|
|
28
28
|
if runnable_migration
|
29
29
|
runner = MigrationRunner.new(runnable_migration)
|
30
|
-
|
30
|
+
|
31
|
+
try_with_lock do
|
32
|
+
runner.run_migration_job
|
33
|
+
end
|
31
34
|
end
|
32
35
|
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
|
33
42
|
end
|
34
43
|
end
|
35
44
|
end
|
@@ -49,12 +49,7 @@ module OnlineMigrations
|
|
49
49
|
|
50
50
|
alias_attribute :name, :migration_name
|
51
51
|
|
52
|
-
|
53
|
-
if Utils.ar_version >= 7
|
54
|
-
enum :status, STATUSES.index_with(&:to_s)
|
55
|
-
else
|
56
|
-
enum status: STATUSES.index_with(&:to_s)
|
57
|
-
end
|
52
|
+
enum :status, STATUSES.index_with(&:to_s)
|
58
53
|
|
59
54
|
belongs_to :parent, class_name: name, optional: true, inverse_of: :children
|
60
55
|
has_many :children, class_name: name, foreign_key: :parent_id, inverse_of: :parent
|
@@ -23,7 +23,10 @@ module OnlineMigrations
|
|
23
23
|
migration = find_migration
|
24
24
|
if migration
|
25
25
|
runner = MigrationRunner.new(migration)
|
26
|
-
|
26
|
+
|
27
|
+
try_with_lock do
|
28
|
+
runner.run
|
29
|
+
end
|
27
30
|
end
|
28
31
|
end
|
29
32
|
|
@@ -40,6 +43,11 @@ module OnlineMigrations
|
|
40
43
|
end
|
41
44
|
end
|
42
45
|
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
|
43
51
|
end
|
44
52
|
end
|
45
53
|
end
|
@@ -118,24 +118,19 @@ module OnlineMigrations
|
|
118
118
|
type_cast_functions[column_name] = type_cast_function if type_cast_function
|
119
119
|
tmp_column_name = conversions[column_name]
|
120
120
|
|
121
|
-
if
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
**old_col_options, **column_options, default: old_col.default || 0, null: false)
|
130
|
-
else
|
131
|
-
if !old_col.default.nil?
|
132
|
-
old_col_options = old_col_options.merge(default: old_col.default, null: old_col.null)
|
133
|
-
end
|
134
|
-
add_column(table_name, tmp_column_name, new_type, **old_col_options, **column_options)
|
135
|
-
end
|
121
|
+
if primary_key(table_name) == column_name.to_s && old_col.type == :integer
|
122
|
+
# For PG < 11 and Primary Key conversions, setting a column as the PK
|
123
|
+
# converts even check constraints to NOT NULL column constraints
|
124
|
+
# and forces an inline re-verification of the whole table.
|
125
|
+
# To avoid this, we instead set it to `NOT NULL DEFAULT 0` and we'll
|
126
|
+
# copy the correct values when backfilling.
|
127
|
+
add_column(table_name, tmp_column_name, new_type,
|
128
|
+
**old_col_options, **column_options, default: old_col.default || 0, null: false)
|
136
129
|
else
|
130
|
+
if !old_col.default.nil?
|
131
|
+
old_col_options = old_col_options.merge(default: old_col.default, null: old_col.null)
|
132
|
+
end
|
137
133
|
add_column(table_name, tmp_column_name, new_type, **old_col_options, **column_options)
|
138
|
-
change_column_default(table_name, tmp_column_name, old_col.default) if !old_col.default.nil?
|
139
134
|
end
|
140
135
|
end
|
141
136
|
|
@@ -264,7 +259,7 @@ module OnlineMigrations
|
|
264
259
|
|
265
260
|
# At this point we are sure there are no NULLs in this column
|
266
261
|
transaction do
|
267
|
-
|
262
|
+
change_column_null(table_name, tmp_column_name, false)
|
268
263
|
remove_not_null_constraint(table_name, tmp_column_name)
|
269
264
|
end
|
270
265
|
end
|
@@ -494,29 +489,6 @@ module OnlineMigrations
|
|
494
489
|
end
|
495
490
|
end
|
496
491
|
|
497
|
-
def __set_not_null(table_name, column_name)
|
498
|
-
# For PG >= 12 we can "promote" CHECK constraint to NOT NULL constraint:
|
499
|
-
# https://github.com/postgres/postgres/commit/bbb96c3704c041d139181c6601e5bc770e045d26
|
500
|
-
if database_version >= 12_00_00
|
501
|
-
execute(<<~SQL)
|
502
|
-
ALTER TABLE #{quote_table_name(table_name)}
|
503
|
-
ALTER #{quote_column_name(column_name)}
|
504
|
-
SET NOT NULL
|
505
|
-
SQL
|
506
|
-
else
|
507
|
-
# For older versions we can set attribute as NOT NULL directly
|
508
|
-
# through PG internal tables.
|
509
|
-
# In-depth analysis of implications of this was made, so this approach
|
510
|
-
# is considered safe - https://habr.com/ru/company/haulmont/blog/493954/ (in russian).
|
511
|
-
execute(<<~SQL)
|
512
|
-
UPDATE pg_catalog.pg_attribute
|
513
|
-
SET attnotnull = true
|
514
|
-
WHERE attrelid = #{quote(table_name)}::regclass
|
515
|
-
AND attname = #{quote(column_name)}
|
516
|
-
SQL
|
517
|
-
end
|
518
|
-
end
|
519
|
-
|
520
492
|
def __rename_constraint(table_name, old_name, new_name)
|
521
493
|
execute(<<~SQL)
|
522
494
|
ALTER TABLE #{quote_table_name(table_name)}
|
@@ -59,7 +59,6 @@ module OnlineMigrations
|
|
59
59
|
short_primary_key_type: "using-primary-key-with-short-integer-type",
|
60
60
|
drop_table_multiple_foreign_keys: "removing-a-table-with-multiple-foreign-keys",
|
61
61
|
rename_table: "renaming-a-table",
|
62
|
-
add_column_with_default_null: "adding-a-column-with-a-default-value",
|
63
62
|
add_column_with_default: "adding-a-column-with-a-default-value",
|
64
63
|
add_column_generated_stored: "adding-a-stored-generated-column",
|
65
64
|
add_column_json: "adding-a-json-column",
|
@@ -69,7 +68,6 @@ module OnlineMigrations
|
|
69
68
|
change_column_null: "setting-not-null-on-an-existing-column",
|
70
69
|
remove_column: "removing-a-column",
|
71
70
|
add_timestamps_with_default: "adding-a-column-with-a-default-value",
|
72
|
-
add_hash_index: "hash-indexes",
|
73
71
|
add_reference: "adding-a-reference",
|
74
72
|
add_index: "adding-an-index-non-concurrently",
|
75
73
|
replace_index: "replacing-an-index",
|
@@ -89,8 +87,8 @@ module OnlineMigrations
|
|
89
87
|
adapter = connection.adapter_name
|
90
88
|
case adapter
|
91
89
|
when /postg/i
|
92
|
-
if postgresql_version < Gem::Version.new("
|
93
|
-
raise "#{adapter} <
|
90
|
+
if postgresql_version < Gem::Version.new("12")
|
91
|
+
raise "#{adapter} < 12 is not supported"
|
94
92
|
end
|
95
93
|
else
|
96
94
|
raise "#{adapter} is not supported"
|
@@ -101,9 +99,12 @@ module OnlineMigrations
|
|
101
99
|
|
102
100
|
def set_statement_timeout
|
103
101
|
if !defined?(@statement_timeout_set)
|
104
|
-
if (
|
105
|
-
#
|
106
|
-
|
102
|
+
if (timeout = OnlineMigrations.config.statement_timeout)
|
103
|
+
# use ceil to prevent no timeout for values under 1 ms
|
104
|
+
timeout = (timeout * 1000).ceil if !timeout.is_a?(String)
|
105
|
+
|
106
|
+
# Can't use `execute`, because command checker marks it as a dangerous operation.
|
107
|
+
connection.select_value("SET statement_timeout TO #{connection.quote(timeout)}")
|
107
108
|
end
|
108
109
|
@statement_timeout_set = true
|
109
110
|
end
|
@@ -181,11 +182,7 @@ module OnlineMigrations
|
|
181
182
|
# But I think this check is enough for now.
|
182
183
|
raise_error :short_primary_key_type if short_primary_key_type?(options)
|
183
184
|
|
184
|
-
if block
|
185
|
-
collect_foreign_keys(&block)
|
186
|
-
check_for_hash_indexes(&block) if postgresql_version < Gem::Version.new("10")
|
187
|
-
end
|
188
|
-
|
185
|
+
collect_foreign_keys(&block) if block
|
189
186
|
@new_tables << table_name.to_s
|
190
187
|
end
|
191
188
|
|
@@ -230,18 +227,11 @@ module OnlineMigrations
|
|
230
227
|
@new_columns << [table_name.to_s, column_name.to_s]
|
231
228
|
|
232
229
|
if !new_or_small_table?(table_name)
|
233
|
-
if options.key?(:default) &&
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
code: command_str(:add_column, table_name, column_name, type, options.except(:default))
|
239
|
-
else
|
240
|
-
raise_error :add_column_with_default,
|
241
|
-
code: command_str(:add_column_with_default, table_name, column_name, type, options),
|
242
|
-
not_null: options[:null] == false,
|
243
|
-
volatile_default: volatile_default
|
244
|
-
end
|
230
|
+
if options.key?(:default) && !default.nil? && (volatile_default = Utils.volatile_default?(connection, type, default))
|
231
|
+
raise_error :add_column_with_default,
|
232
|
+
code: command_str(:add_column_with_default, table_name, column_name, type, options),
|
233
|
+
not_null: options[:null] == false,
|
234
|
+
volatile_default: volatile_default
|
245
235
|
end
|
246
236
|
|
247
237
|
if type == :virtual && options[:stored]
|
@@ -280,10 +270,7 @@ module OnlineMigrations
|
|
280
270
|
table_name: table_name,
|
281
271
|
column_name: column_name,
|
282
272
|
new_column: new_column,
|
283
|
-
model: table_name.to_s.classify
|
284
|
-
partial_writes: Utils.ar_partial_writes?,
|
285
|
-
partial_writes_setting: Utils.ar_partial_writes_setting,
|
286
|
-
enumerate_columns_in_select_statements: Utils.ar_enumerate_columns_in_select_statements
|
273
|
+
model: table_name.to_s.classify
|
287
274
|
end
|
288
275
|
end
|
289
276
|
|
@@ -360,9 +347,7 @@ module OnlineMigrations
|
|
360
347
|
|
361
348
|
[:datetime, :timestamp, :timestamptz].include?(existing_type) &&
|
362
349
|
precision >= existing_precision &&
|
363
|
-
(type == existing_type ||
|
364
|
-
(postgresql_version >= Gem::Version.new("12") &&
|
365
|
-
connection.select_value("SHOW timezone") == "UTC"))
|
350
|
+
(type == existing_type || connection.select_value("SHOW timezone") == "UTC")
|
366
351
|
when :interval
|
367
352
|
precision = options[:precision] || options[:limit] || 6
|
368
353
|
existing_precision = existing_column.precision || existing_column.limit || 6
|
@@ -394,22 +379,18 @@ module OnlineMigrations
|
|
394
379
|
end
|
395
380
|
|
396
381
|
def change_column_default(table_name, column_name, _default_or_changes)
|
397
|
-
if
|
398
|
-
raise_error :change_column_default
|
399
|
-
config: Utils.ar_partial_writes_setting
|
382
|
+
if ActiveRecord::Base.partial_inserts && !new_column?(table_name, column_name)
|
383
|
+
raise_error :change_column_default
|
400
384
|
end
|
401
385
|
end
|
402
386
|
|
403
387
|
def change_column_null(table_name, column_name, allow_null, default = nil, **)
|
404
388
|
if !allow_null && !new_or_small_table?(table_name)
|
405
|
-
safe = false
|
406
389
|
# In PostgreSQL 12+ you can add a check constraint to the table
|
407
390
|
# and then "promote" it to NOT NULL for the column.
|
408
|
-
|
409
|
-
|
410
|
-
c["def"] == "CHECK ((#{column_name} IS NOT NULL))"
|
411
|
-
c["def"] == "CHECK ((#{connection.quote_column_name(column_name)} IS NOT NULL))"
|
412
|
-
end
|
391
|
+
safe = check_constraints(table_name).any? do |c|
|
392
|
+
c["def"] == "CHECK ((#{column_name} IS NOT NULL))" ||
|
393
|
+
c["def"] == "CHECK ((#{connection.quote_column_name(column_name)} IS NOT NULL))"
|
413
394
|
end
|
414
395
|
|
415
396
|
if !safe
|
@@ -417,17 +398,13 @@ module OnlineMigrations
|
|
417
398
|
vars = {
|
418
399
|
add_constraint_code: command_str(:add_not_null_constraint, table_name, column_name, name: constraint_name, validate: false),
|
419
400
|
validate_constraint_code: command_str(:validate_not_null_constraint, table_name, column_name, name: constraint_name),
|
420
|
-
remove_constraint_code: nil,
|
421
401
|
table_name: table_name,
|
422
402
|
column_name: column_name,
|
423
403
|
default: default,
|
404
|
+
remove_constraint_code: command_str(:remove_check_constraint, table_name, name: constraint_name),
|
405
|
+
change_column_null_code: command_str(:change_column_null, table_name, column_name, false),
|
424
406
|
}
|
425
407
|
|
426
|
-
if postgresql_version >= Gem::Version.new("12")
|
427
|
-
vars[:remove_constraint_code] = command_str(:remove_check_constraint, table_name, name: constraint_name)
|
428
|
-
vars[:change_column_null_code] = command_str(:change_column_null, table_name, column_name, false)
|
429
|
-
end
|
430
|
-
|
431
408
|
raise_error :change_column_null, **vars
|
432
409
|
end
|
433
410
|
end
|
@@ -470,15 +447,13 @@ module OnlineMigrations
|
|
470
447
|
@new_columns << [table_name.to_s, "created_at"]
|
471
448
|
@new_columns << [table_name.to_s, "updated_at"]
|
472
449
|
|
473
|
-
volatile_default = false
|
474
450
|
if !new_or_small_table?(table_name) && !options[:default].nil? &&
|
475
|
-
|
451
|
+
Utils.volatile_default?(connection, :datetime, options[:default])
|
476
452
|
|
477
453
|
raise_error :add_timestamps_with_default,
|
478
454
|
code: [command_str(:add_column_with_default, table_name, :created_at, :datetime, options),
|
479
455
|
command_str(:add_column_with_default, table_name, :updated_at, :datetime, options)].join("\n "),
|
480
|
-
not_null: options[:null] == false
|
481
|
-
volatile_default: volatile_default
|
456
|
+
not_null: options[:null] == false
|
482
457
|
end
|
483
458
|
end
|
484
459
|
|
@@ -486,10 +461,6 @@ module OnlineMigrations
|
|
486
461
|
# Always added by default in 5.0+
|
487
462
|
index = options.fetch(:index, true)
|
488
463
|
|
489
|
-
if index.is_a?(Hash) && index[:using].to_s == "hash" && postgresql_version < Gem::Version.new("10")
|
490
|
-
raise_error :add_hash_index
|
491
|
-
end
|
492
|
-
|
493
464
|
concurrently_set = index.is_a?(Hash) && index[:algorithm] == :concurrently
|
494
465
|
bad_index = index && !concurrently_set
|
495
466
|
|
@@ -522,13 +493,6 @@ module OnlineMigrations
|
|
522
493
|
alias add_belongs_to add_reference
|
523
494
|
|
524
495
|
def add_reference_concurrently(table_name, ref_name, **options)
|
525
|
-
# Always added by default in 5.0+
|
526
|
-
index = options.fetch(:index, true)
|
527
|
-
|
528
|
-
if index.is_a?(Hash) && index[:using].to_s == "hash" && postgresql_version < Gem::Version.new("10")
|
529
|
-
raise_error :add_hash_index
|
530
|
-
end
|
531
|
-
|
532
496
|
foreign_key = options.fetch(:foreign_key, false)
|
533
497
|
|
534
498
|
if foreign_key
|
@@ -546,10 +510,6 @@ module OnlineMigrations
|
|
546
510
|
end
|
547
511
|
|
548
512
|
def add_index(table_name, column_name, **options)
|
549
|
-
if options[:using].to_s == "hash" && postgresql_version < Gem::Version.new("10")
|
550
|
-
raise_error :add_hash_index
|
551
|
-
end
|
552
|
-
|
553
513
|
if !new_or_small_table?(table_name)
|
554
514
|
if options[:algorithm] != :concurrently
|
555
515
|
raise_error :add_index,
|
@@ -702,19 +662,6 @@ module OnlineMigrations
|
|
702
662
|
@foreign_key_tables |= collector.referenced_tables
|
703
663
|
end
|
704
664
|
|
705
|
-
def check_for_hash_indexes(&block)
|
706
|
-
indexes = collect_indexes(&block)
|
707
|
-
if indexes.any? { |index| index.using == "hash" }
|
708
|
-
raise_error :add_hash_index
|
709
|
-
end
|
710
|
-
end
|
711
|
-
|
712
|
-
def collect_indexes(&block)
|
713
|
-
collector = IndexesCollector.new
|
714
|
-
collector.collect(&block)
|
715
|
-
collector.indexes
|
716
|
-
end
|
717
|
-
|
718
665
|
def new_or_small_table?(table_name)
|
719
666
|
new_table?(table_name) || small_table?(table_name)
|
720
667
|
end
|
@@ -736,7 +683,7 @@ module OnlineMigrations
|
|
736
683
|
if Utils.developer_env? && (target_version = OnlineMigrations.config.target_version)
|
737
684
|
target_version.to_s
|
738
685
|
else
|
739
|
-
database_version = connection.
|
686
|
+
database_version = connection.database_version
|
740
687
|
major = database_version / 10000
|
741
688
|
if database_version >= 100000
|
742
689
|
minor = database_version % 10000
|
@@ -128,12 +128,12 @@ migration_helpers provides a safer approach to do this:
|
|
128
128
|
<%= column_name.to_s.inspect %> => <%= new_column.to_s.inspect %>
|
129
129
|
}
|
130
130
|
}
|
131
|
-
<% unless
|
131
|
+
<% unless ActiveRecord::Base.partial_inserts %>
|
132
132
|
|
133
133
|
NOTE: You also need to temporarily enable partial writes (is disabled by default in Active Record >= 7)
|
134
134
|
until the process of column rename is fully done.
|
135
135
|
# config/application.rb
|
136
|
-
config.active_record
|
136
|
+
config.active_record.partial_inserts = true
|
137
137
|
<% end %>
|
138
138
|
|
139
139
|
2. Deploy
|
@@ -148,7 +148,7 @@ It will use a combination of a VIEW and column aliasing to work with both column
|
|
148
148
|
end
|
149
149
|
|
150
150
|
4. Replace usages of the old column with a new column in the codebase
|
151
|
-
<% if enumerate_columns_in_select_statements %>
|
151
|
+
<% if ActiveRecord::Base.enumerate_columns_in_select_statements %>
|
152
152
|
5. Ignore old column
|
153
153
|
|
154
154
|
self.ignored_columns += [:<%= column_name %>]
|
@@ -247,7 +247,7 @@ during writes works automatically). For most column type changes, this does not
|
|
247
247
|
to be inserted when changing the default value of a column.
|
248
248
|
Disable partial writes in config/application.rb:
|
249
249
|
|
250
|
-
config.active_record
|
250
|
+
config.active_record.partial_inserts = false",
|
251
251
|
|
252
252
|
change_column_null:
|
253
253
|
"Setting NOT NULL on an existing column blocks reads and writes while every row is checked.
|
@@ -317,7 +317,7 @@ A safer approach is to:
|
|
317
317
|
<% end %>",
|
318
318
|
|
319
319
|
add_timestamps_with_default:
|
320
|
-
"Adding
|
320
|
+
"Adding timestamp columns with volatile defaults blocks reads and writes while the entire table is rewritten.
|
321
321
|
|
322
322
|
A safer approach is to, for both timestamps columns:
|
323
323
|
1. add the column without a default value
|
@@ -327,7 +327,6 @@ A safer approach is to, for both timestamps columns:
|
|
327
327
|
4. add the NOT NULL constraint
|
328
328
|
<% end %>
|
329
329
|
|
330
|
-
<% unless volatile_default %>
|
331
330
|
add_column_with_default takes care of all this steps:
|
332
331
|
|
333
332
|
class <%= migration_name %> < <%= migration_parent %>
|
@@ -336,8 +335,7 @@ class <%= migration_name %> < <%= migration_parent %>
|
|
336
335
|
def change
|
337
336
|
<%= code %>
|
338
337
|
end
|
339
|
-
end
|
340
|
-
<% end %>",
|
338
|
+
end",
|
341
339
|
|
342
340
|
add_reference:
|
343
341
|
"<% if bad_foreign_key %>
|
@@ -356,13 +354,6 @@ class <%= migration_name %> < <%= migration_parent %>
|
|
356
354
|
end
|
357
355
|
end",
|
358
356
|
|
359
|
-
add_hash_index:
|
360
|
-
"Hash index operations are not WAL-logged, so hash indexes might need to be rebuilt with REINDEX
|
361
|
-
after a database crash if there were unwritten changes. Also, changes to hash indexes are not replicated
|
362
|
-
over streaming or file-based replication after the initial base backup, so they give wrong answers
|
363
|
-
to queries that subsequently use them. For these reasons, hash index use is discouraged.
|
364
|
-
Use B-tree indexes instead.",
|
365
|
-
|
366
357
|
add_index:
|
367
358
|
"Adding an index non-concurrently blocks writes. Instead, use:
|
368
359
|
|
@@ -12,7 +12,7 @@ module OnlineMigrations
|
|
12
12
|
# @param column_name [String, Symbol]
|
13
13
|
# @param value value for the column. It is typically a literal. To perform a computed
|
14
14
|
# update, an Arel literal can be used instead
|
15
|
-
# @option options [Integer] :batch_size (
|
15
|
+
# @option options [Integer] :batch_size (1_000) size of the batch
|
16
16
|
# @option options [String, Symbol] :batch_column_name (primary key) option is for tables without primary key, in this
|
17
17
|
# case another unique integer column can be used. Example: `:user_id`
|
18
18
|
# @option options [Proc, Boolean] :progress (false) whether to show progress while running.
|
@@ -439,9 +439,7 @@ module OnlineMigrations
|
|
439
439
|
def add_column_with_default(table_name, column_name, type, **options)
|
440
440
|
default = options.fetch(:default)
|
441
441
|
|
442
|
-
if
|
443
|
-
add_column(table_name, column_name, type, **options)
|
444
|
-
else
|
442
|
+
if Utils.volatile_default?(self, type, default)
|
445
443
|
__ensure_not_in_transaction!
|
446
444
|
|
447
445
|
batch_options = options.extract!(:batch_size, :batch_column_name, :progress, :pause_ms)
|
@@ -465,12 +463,12 @@ module OnlineMigrations
|
|
465
463
|
add_not_null_constraint(table_name, column_name, validate: false)
|
466
464
|
validate_not_null_constraint(table_name, column_name)
|
467
465
|
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
remove_not_null_constraint(table_name, column_name)
|
472
|
-
end
|
466
|
+
# In PostgreSQL 12+ it is safe to "promote" a CHECK constraint to `NOT NULL` for the column
|
467
|
+
change_column_null(table_name, column_name, false)
|
468
|
+
remove_not_null_constraint(table_name, column_name)
|
473
469
|
end
|
470
|
+
else
|
471
|
+
add_column(table_name, column_name, type, **options)
|
474
472
|
end
|
475
473
|
end
|
476
474
|
|
@@ -729,23 +727,10 @@ module OnlineMigrations
|
|
729
727
|
end
|
730
728
|
end
|
731
729
|
|
732
|
-
|
733
|
-
|
734
|
-
|
735
|
-
|
736
|
-
super
|
737
|
-
else
|
738
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
739
|
-
Running `add_index` without a statement timeout is deprecated.
|
740
|
-
Configure an explicit statement timeout in the initializer file via `config.statement_timeout`
|
741
|
-
or the default database statement timeout will be used.
|
742
|
-
Example, `config.statement_timeout = 1.hour`.
|
743
|
-
MSG
|
744
|
-
|
745
|
-
disable_statement_timeout do
|
746
|
-
super
|
747
|
-
end
|
748
|
-
end
|
730
|
+
# "CREATE INDEX CONCURRENTLY" requires a "SHARE UPDATE EXCLUSIVE" lock.
|
731
|
+
# It only conflicts with constraint validations, creating/removing indexes,
|
732
|
+
# and some other "ALTER TABLE"s.
|
733
|
+
super
|
749
734
|
end
|
750
735
|
|
751
736
|
# Extends default method to be idempotent.
|
@@ -770,23 +755,10 @@ module OnlineMigrations
|
|
770
755
|
end
|
771
756
|
|
772
757
|
if index_exists
|
773
|
-
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
super
|
778
|
-
else
|
779
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
780
|
-
Running `remove_index` without a statement timeout is deprecated.
|
781
|
-
Configure an explicit statement timeout in the initializer file via `config.statement_timeout`
|
782
|
-
or the default database statement timeout will be used.
|
783
|
-
Example, `config.statement_timeout = 1.hour`.
|
784
|
-
MSG
|
785
|
-
|
786
|
-
disable_statement_timeout do
|
787
|
-
super
|
788
|
-
end
|
789
|
-
end
|
758
|
+
# "DROP INDEX CONCURRENTLY" requires a "SHARE UPDATE EXCLUSIVE" lock.
|
759
|
+
# It only conflicts with constraint validations, other creating/removing indexes,
|
760
|
+
# and some "ALTER TABLE"s.
|
761
|
+
super
|
790
762
|
else
|
791
763
|
Utils.say("Index was not removed because it does not exist.")
|
792
764
|
end
|
@@ -835,23 +807,10 @@ module OnlineMigrations
|
|
835
807
|
# Skip costly operation if already validated.
|
836
808
|
return if foreign_key.validated?
|
837
809
|
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
842
|
-
super
|
843
|
-
else
|
844
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
845
|
-
Running `validate_foreign_key` without a statement timeout is deprecated.
|
846
|
-
Configure an explicit statement timeout in the initializer file via `config.statement_timeout`
|
847
|
-
or the default database statement timeout will be used.
|
848
|
-
Example, `config.statement_timeout = 1.hour`.
|
849
|
-
MSG
|
850
|
-
|
851
|
-
disable_statement_timeout do
|
852
|
-
super
|
853
|
-
end
|
854
|
-
end
|
810
|
+
# "VALIDATE CONSTRAINT" requires a "SHARE UPDATE EXCLUSIVE" lock.
|
811
|
+
# It only conflicts with other validations, creating/removing indexes,
|
812
|
+
# and some other "ALTER TABLE"s.
|
813
|
+
super
|
855
814
|
end
|
856
815
|
|
857
816
|
# Extends default method to be idempotent.
|
@@ -894,23 +853,10 @@ module OnlineMigrations
|
|
894
853
|
# Skip costly operation if already validated.
|
895
854
|
return if check_constraint.validated?
|
896
855
|
|
897
|
-
|
898
|
-
|
899
|
-
|
900
|
-
|
901
|
-
super
|
902
|
-
else
|
903
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
904
|
-
Running `validate_check_constraint` without a statement timeout is deprecated.
|
905
|
-
Configure an explicit statement timeout in the initializer file via `config.statement_timeout`
|
906
|
-
or the default database statement timeout will be used.
|
907
|
-
Example, `config.statement_timeout = 1.hour`.
|
908
|
-
MSG
|
909
|
-
|
910
|
-
disable_statement_timeout do
|
911
|
-
super
|
912
|
-
end
|
913
|
-
end
|
856
|
+
# "VALIDATE CONSTRAINT" requires a "SHARE UPDATE EXCLUSIVE" lock.
|
857
|
+
# It only conflicts with other validations, creating/removing indexes,
|
858
|
+
# and some other "ALTER TABLE"s.
|
859
|
+
super
|
914
860
|
end
|
915
861
|
|
916
862
|
# Extends default method to be idempotent
|
@@ -965,28 +911,6 @@ module OnlineMigrations
|
|
965
911
|
end
|
966
912
|
end
|
967
913
|
|
968
|
-
# @private
|
969
|
-
def disable_statement_timeout
|
970
|
-
OnlineMigrations.deprecator.warn(<<~MSG)
|
971
|
-
`disable_statement_timeout` is deprecated and will be removed. Configure an explicit
|
972
|
-
statement timeout in the initializer file via `config.statement_timeout` or the default
|
973
|
-
database statement timeout will be used. Example, `config.statement_timeout = 1.hour`.
|
974
|
-
MSG
|
975
|
-
|
976
|
-
prev_value = select_value("SHOW statement_timeout")
|
977
|
-
__set_statement_timeout(0)
|
978
|
-
yield
|
979
|
-
ensure
|
980
|
-
__set_statement_timeout(prev_value)
|
981
|
-
end
|
982
|
-
|
983
|
-
# @private
|
984
|
-
def __set_statement_timeout(timeout)
|
985
|
-
# use ceil to prevent no timeout for values under 1 ms
|
986
|
-
timeout = (timeout.to_f * 1000).ceil if !timeout.is_a?(String)
|
987
|
-
execute("SET statement_timeout TO #{quote(timeout)}")
|
988
|
-
end
|
989
|
-
|
990
914
|
# @private
|
991
915
|
# Executes the block with a retry mechanism that alters the `lock_timeout`
|
992
916
|
# and sleep time between attempts.
|
@@ -86,26 +86,6 @@ module OnlineMigrations
|
|
86
86
|
"#{short_name}#{hashed_identifier}"
|
87
87
|
end
|
88
88
|
|
89
|
-
def ar_partial_writes?
|
90
|
-
ActiveRecord::Base.public_send(ar_partial_writes_setting)
|
91
|
-
end
|
92
|
-
|
93
|
-
def ar_partial_writes_setting
|
94
|
-
if Utils.ar_version >= 7.0
|
95
|
-
"partial_inserts"
|
96
|
-
else
|
97
|
-
"partial_writes"
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
def ar_enumerate_columns_in_select_statements
|
102
|
-
if ar_version >= 7
|
103
|
-
ActiveRecord::Base.enumerate_columns_in_select_statements
|
104
|
-
else
|
105
|
-
false
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
89
|
# Returns estimated rows count for a table.
|
110
90
|
# https://www.citusdata.com/blog/2016/10/12/count-performance/
|
111
91
|
def estimated_count(connection, table_name)
|
@@ -6,7 +6,7 @@ module OnlineMigrations
|
|
6
6
|
class << self
|
7
7
|
def enable
|
8
8
|
@activerecord_logger_was = ActiveRecord::Base.logger
|
9
|
-
@verbose_query_logs_was = verbose_query_logs
|
9
|
+
@verbose_query_logs_was = ActiveRecord.verbose_query_logs
|
10
10
|
return if @activerecord_logger_was.nil?
|
11
11
|
|
12
12
|
stdout_logger = ActiveSupport::Logger.new($stdout)
|
@@ -23,30 +23,13 @@ module OnlineMigrations
|
|
23
23
|
end
|
24
24
|
|
25
25
|
ActiveRecord::Base.logger = combined_logger
|
26
|
-
|
26
|
+
ActiveRecord.verbose_query_logs = false
|
27
27
|
end
|
28
28
|
|
29
29
|
def disable
|
30
30
|
ActiveRecord::Base.logger = @activerecord_logger_was
|
31
|
-
|
31
|
+
ActiveRecord.verbose_query_logs = @verbose_query_logs_was
|
32
32
|
end
|
33
|
-
|
34
|
-
private
|
35
|
-
def verbose_query_logs
|
36
|
-
if Utils.ar_version >= 7.0
|
37
|
-
ActiveRecord.verbose_query_logs
|
38
|
-
else
|
39
|
-
ActiveRecord::Base.verbose_query_logs
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def set_verbose_query_logs(value) # rubocop:disable Naming/AccessorMethodName
|
44
|
-
if Utils.ar_version >= 7.0
|
45
|
-
ActiveRecord.verbose_query_logs = value
|
46
|
-
else
|
47
|
-
ActiveRecord::Base.verbose_query_logs = value
|
48
|
-
end
|
49
|
-
end
|
50
33
|
end
|
51
34
|
end
|
52
35
|
end
|
data/lib/online_migrations.rb
CHANGED
@@ -23,12 +23,12 @@ module OnlineMigrations
|
|
23
23
|
|
24
24
|
extend ActiveSupport::Autoload
|
25
25
|
|
26
|
+
autoload :AdvisoryLock
|
26
27
|
autoload :ApplicationRecord
|
27
28
|
autoload :BatchIterator
|
28
29
|
autoload :VerboseSqlLogs
|
29
30
|
autoload :ForeignKeysCollector
|
30
31
|
autoload :IndexDefinition
|
31
|
-
autoload :IndexesCollector
|
32
32
|
autoload :CommandChecker
|
33
33
|
autoload :BackgroundMigration
|
34
34
|
|
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.23.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-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,14 +16,14 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '7.0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '7.0'
|
27
27
|
description:
|
28
28
|
email:
|
29
29
|
- fatkodima123@gmail.com
|
@@ -48,6 +48,7 @@ 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
|
51
52
|
- lib/online_migrations/application_record.rb
|
52
53
|
- lib/online_migrations/background_migration.rb
|
53
54
|
- lib/online_migrations/background_migrations/backfill_column.rb
|
@@ -82,7 +83,6 @@ files:
|
|
82
83
|
- lib/online_migrations/error_messages.rb
|
83
84
|
- lib/online_migrations/foreign_keys_collector.rb
|
84
85
|
- lib/online_migrations/index_definition.rb
|
85
|
-
- lib/online_migrations/indexes_collector.rb
|
86
86
|
- lib/online_migrations/lock_retrier.rb
|
87
87
|
- lib/online_migrations/migration.rb
|
88
88
|
- lib/online_migrations/migrator.rb
|
@@ -107,7 +107,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
107
107
|
requirements:
|
108
108
|
- - ">="
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
110
|
+
version: '3.0'
|
111
111
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
112
112
|
requirements:
|
113
113
|
- - ">="
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module OnlineMigrations
|
4
|
-
# @private
|
5
|
-
class IndexesCollector
|
6
|
-
COLUMN_TYPES = [:bigint, :binary, :boolean, :date, :datetime, :decimal,
|
7
|
-
:float, :integer, :json, :string, :text, :time, :timestamp, :virtual]
|
8
|
-
|
9
|
-
attr_reader :indexes
|
10
|
-
|
11
|
-
def initialize
|
12
|
-
@indexes = []
|
13
|
-
end
|
14
|
-
|
15
|
-
def collect
|
16
|
-
yield self
|
17
|
-
end
|
18
|
-
|
19
|
-
def index(_column_name, **options)
|
20
|
-
@indexes << IndexDefinition.new(using: options[:using].to_s)
|
21
|
-
end
|
22
|
-
|
23
|
-
def references(*_ref_names, **options)
|
24
|
-
index = options.fetch(:index, true)
|
25
|
-
|
26
|
-
if index
|
27
|
-
using = index.is_a?(Hash) ? index[:using].to_s : nil
|
28
|
-
@indexes << IndexDefinition.new(using: using)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
alias belongs_to references
|
32
|
-
|
33
|
-
def method_missing(method_name, *_args, **options)
|
34
|
-
# Check for type-based methods, where we can also specify an index:
|
35
|
-
# t.string :email, index: true
|
36
|
-
if COLUMN_TYPES.include?(method_name)
|
37
|
-
index = options.fetch(:index, false)
|
38
|
-
|
39
|
-
if index
|
40
|
-
using = index.is_a?(Hash) ? index[:using].to_s : nil
|
41
|
-
@indexes << IndexDefinition.new(using: using)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|