safe-pg-migrations 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d806334c473708774d2180dbe91d2483b49f0d27b0a223a2973bc95d986d4030
4
- data.tar.gz: 9b5c2d329d5cc1bd12c80944420e20eb5454e5b0a0f7fda2d6ec3fc3c3a5e042
3
+ metadata.gz: e6c0ad1795c94252929d907f94e3cf65617ca9a353414d031fb74a852ee294ea
4
+ data.tar.gz: f2d1045f55ff1896ef045edb4fe2868b7f26bc4e1b7cc542907eaab08da91075
5
5
  SHA512:
6
- metadata.gz: 6786e68cce9dbd91a4633c80526a1fd63f6fbfa335d6e2b53d29b71ba0b566f9bb32169745bb13f89a059b82b0c0b21585a9bffdb9ebc751aa0e4a467f0f7934
7
- data.tar.gz: 299a69f1d9413279fd61908a7d8f6eb65bd4c9912abcc1101ca8acbfee39c40d33086efa28a9f2133c0679c1ad9cdbbfe3ce84338f2175ad882b7d1ace707654
6
+ metadata.gz: 3b32973e6920a5db9d7d48440cc5ac09fbdd511539dcf3ec436584584d6a9b501a1027a212d991a27e47ae9a20e471cabc6d705e42f6324eb79eb1b529186f1f
7
+ data.tar.gz: 0c006975e25f1c318caa3375a05c1aa43a45c50813ef6b03a4d4d7d842b5c5a2188243744a8f4e769196c39ef6ca162c693a22b48dec1c38f13aa4b1d61bfc80
data/README.md CHANGED
@@ -122,7 +122,7 @@ Note: the addition of the not null constraint may timeout. In that case, you may
122
122
 
123
123
  </details>
124
124
 
125
- <details><summary>Safe <code>add_index</code> and <code>remove_index</code></summary>
125
+ <details><summary id="safe_add_remove_index">Safe <code>add_index</code> and <code>remove_index</code></summary>
126
126
 
127
127
  Creating an index requires a `SHARE` lock on the target table which blocks all write on the table while the index is created (which can take some time on a large table). This is usually not practical in a live environment. Thus, **Safe PG Migrations** ensures indexes are created concurrently.
128
128
 
@@ -135,7 +135,7 @@ If you still get lock timeout while adding / removing indexes, it might be for o
135
135
 
136
136
  </details>
137
137
 
138
- <details><summary>safe <code>add_foreign_key</code> (and <code>add_reference</code>)</summary>
138
+ <details><summary id="safe_add_foreign_key">safe <code>add_foreign_key</code> (and <code>add_reference</code>)</summary>
139
139
 
140
140
  Adding a foreign key requires a `SHARE ROW EXCLUSIVE` lock, which **prevent writing in the tables** while the migration is running.
141
141
 
@@ -206,9 +206,7 @@ So you can actually check that the `CREATE INDEX` statement will be performed co
206
206
  ```ruby
207
207
  SafePgMigrations.config.safe_timeout = 5.seconds # Lock and statement timeout used for all DDL operations except from CREATE / DROP INDEX
208
208
 
209
- SafePgMigrations.config.index_lock_timeout = 30.seconds # Lock timeout used for CREATE / DROP INDEX
210
-
211
- SafePgMigrations.config.blocking_activity_logger_margin = 1.second # Delay to output blocking queries before timeout. Must be smaller than safe_timeout and index_lock_timeout
209
+ SafePgMigrations.config.blocking_activity_logger_margin = 1.second # Delay to output blocking queries before timeout. Must be shorter than safe_timeout
212
210
 
213
211
  SafePgMigrations.config.batch_size = 1000 # Size of the batches used for backfilling when adding a column with a default value pre-PG11
214
212
 
@@ -6,6 +6,7 @@ require 'safe-pg-migrations/plugins/blocking_activity_logger'
6
6
  require 'safe-pg-migrations/plugins/statement_insurer'
7
7
  require 'safe-pg-migrations/plugins/statement_retrier'
8
8
  require 'safe-pg-migrations/plugins/idem_potent_statements'
9
+ require 'safe-pg-migrations/plugins/useless_statements_logger'
9
10
 
10
11
  module SafePgMigrations
11
12
  # Order matters: the bottom-most plugin will have precedence
@@ -14,6 +15,7 @@ module SafePgMigrations
14
15
  IdemPotentStatements,
15
16
  StatementRetrier,
16
17
  StatementInsurer,
18
+ UselessStatementsLogger,
17
19
  ].freeze
18
20
 
19
21
  class << self
@@ -78,6 +80,7 @@ module SafePgMigrations
78
80
  end
79
81
 
80
82
  def disable_ddl_transaction
83
+ UselessStatementsLogger.warn_useless '`disable_ddl_transaction`' if super
81
84
  true
82
85
  end
83
86
 
@@ -5,7 +5,6 @@ require 'active_support/core_ext/numeric/time'
5
5
  module SafePgMigrations
6
6
  class Configuration
7
7
  attr_accessor :safe_timeout
8
- attr_accessor :index_lock_timeout
9
8
  attr_accessor :blocking_activity_logger_margin
10
9
  attr_accessor :batch_size
11
10
  attr_accessor :retry_delay
@@ -13,7 +12,6 @@ module SafePgMigrations
13
12
 
14
13
  def initialize
15
14
  self.safe_timeout = 5.seconds
16
- self.index_lock_timeout = 30.seconds
17
15
  self.blocking_activity_logger_margin = 1.second
18
16
  self.batch_size = 1000
19
17
  self.retry_delay = 1.minute
@@ -24,10 +22,6 @@ module SafePgMigrations
24
22
  pg_duration(safe_timeout)
25
23
  end
26
24
 
27
- def pg_index_lock_timeout
28
- pg_duration(index_lock_timeout)
29
- end
30
-
31
25
  def pg_duration(duration)
32
26
  value, unit = duration.integer? ? [duration, 's'] : [(duration * 1000).to_i, 'ms']
33
27
  "#{value}#{unit}"
@@ -3,7 +3,7 @@
3
3
  module SafePgMigrations
4
4
  module BlockingActivityLogger
5
5
  SELECT_BLOCKING_QUERIES_SQL = <<~SQL.squish
6
- SELECT blocking_activity.query
6
+ SELECT blocking_activity.query, blocked_activity.xact_start as start
7
7
  FROM pg_catalog.pg_locks blocked_locks
8
8
  JOIN pg_catalog.pg_stat_activity blocked_activity
9
9
  ON blocked_activity.pid = blocked_locks.pid
@@ -25,32 +25,23 @@ module SafePgMigrations
25
25
  SQL
26
26
 
27
27
  %i[
28
- add_column remove_column add_foreign_key remove_foreign_key change_column_default
29
- change_column_null create_table add_index remove_index
28
+ add_column remove_column add_foreign_key remove_foreign_key change_column_default change_column_null create_table
30
29
  ].each do |method|
31
30
  define_method method do |*args, &block|
32
- log_blocking_queries(method) { super(*args, &block) }
31
+ log_blocking_queries { super(*args, &block) }
33
32
  end
34
33
  end
35
34
 
36
35
  private
37
36
 
38
- def delay_before_logging(method)
39
- timeout_delay =
40
- if %i[add_index remove_index].include?(method)
41
- SafePgMigrations.config.index_lock_timeout
42
- else
43
- SafePgMigrations.config.safe_timeout
44
- end
45
-
46
- timeout_delay - SafePgMigrations.config.blocking_activity_logger_margin
47
- end
37
+ def log_blocking_queries
38
+ delay_before_logging =
39
+ SafePgMigrations.config.safe_timeout - SafePgMigrations.config.blocking_activity_logger_margin
48
40
 
49
- def log_blocking_queries(method)
50
41
  blocking_queries_retriever_thread =
51
42
  Thread.new do
52
- sleep delay_before_logging(method)
53
- SafePgMigrations.alternate_connection.query_values(SELECT_BLOCKING_QUERIES_SQL % raw_connection.backend_pid)
43
+ sleep delay_before_logging
44
+ SafePgMigrations.alternate_connection.query(SELECT_BLOCKING_QUERIES_SQL % raw_connection.backend_pid)
54
45
  end
55
46
 
56
47
  yield
@@ -75,7 +66,7 @@ module SafePgMigrations
75
66
  "Statement was being blocked by the following #{'query'.pluralize(queries.size)}:", true
76
67
  )
77
68
  SafePgMigrations.say '', true
78
- queries.each { |query| SafePgMigrations.say " #{query}", true }
69
+ queries.each { |query, start_time| SafePgMigrations.say "#{format_start_time start_time}: #{query}", true }
79
70
  SafePgMigrations.say(
80
71
  'Beware, some of those queries might run in a transaction. In this case the locking query might be '\
81
72
  'located elsewhere in the transaction',
@@ -86,5 +77,10 @@ module SafePgMigrations
86
77
 
87
78
  raise
88
79
  end
80
+
81
+ def format_start_time(start_time, reference_time = Time.now)
82
+ duration = (reference_time - start_time).round
83
+ "transaction started #{duration} #{'second'.pluralize(duration)} ago"
84
+ end
89
85
  end
90
86
  end
@@ -43,12 +43,19 @@ module SafePgMigrations
43
43
  end
44
44
 
45
45
  def create_table(table_name, comment: nil, **options)
46
- return super unless table_exists?(table_name)
46
+ return super if options[:force] || !table_exists?(table_name)
47
47
 
48
- SafePgMigrations.say(
49
- "/!\\ Table '#{table_name}' already exists. Skipping statement.",
50
- true
51
- )
48
+ SafePgMigrations.say "/!\\ Table '#{table_name}' already exists.", true
49
+
50
+ td = create_table_definition(table_name, **options)
51
+
52
+ yield td if block_given?
53
+
54
+ SafePgMigrations.say(td.indexes.empty? ? '-- Skipping statement' : '-- Creating indexes', true)
55
+
56
+ td.indexes.each do |column_name, index_options|
57
+ add_index(table_name, column_name, index_options)
58
+ end
52
59
  end
53
60
 
54
61
  private
@@ -50,7 +50,7 @@ module SafePgMigrations
50
50
  options[:algorithm] = :concurrently
51
51
  SafePgMigrations.say_method_call(:add_index, table_name, column_name, options)
52
52
 
53
- with_index_timeouts { super }
53
+ without_timeout { super }
54
54
  end
55
55
 
56
56
  def remove_index(table_name, options = {})
@@ -58,7 +58,7 @@ module SafePgMigrations
58
58
  options[:algorithm] = :concurrently
59
59
  SafePgMigrations.say_method_call(:remove_index, table_name, options)
60
60
 
61
- with_index_timeouts { super }
61
+ without_timeout { super }
62
62
  end
63
63
 
64
64
  def backfill_column_default(table_name, column_name)
@@ -99,12 +99,12 @@ module SafePgMigrations
99
99
  with_setting(:statement_timeout, 0) { yield }
100
100
  end
101
101
 
102
- def with_index_timeouts
103
- without_statement_timeout do
104
- with_setting(:lock_timeout, SafePgMigrations.config.pg_index_lock_timeout) do
105
- yield
106
- end
107
- end
102
+ def without_lock_timeout
103
+ with_setting(:lock_timeout, 0) { yield }
104
+ end
105
+
106
+ def without_timeout
107
+ without_statement_timeout { without_lock_timeout { yield } }
108
108
  end
109
109
  end
110
110
  end
@@ -3,8 +3,7 @@
3
3
  module SafePgMigrations
4
4
  module StatementRetrier
5
5
  RETRIABLE_SCHEMA_STATEMENTS = %i[
6
- add_column add_foreign_key remove_foreign_key change_column_default
7
- change_column_null add_index remove_index remove_column
6
+ add_column add_foreign_key remove_foreign_key change_column_default change_column_null remove_column
8
7
  ].freeze
9
8
 
10
9
  RETRIABLE_SCHEMA_STATEMENTS.each do |method|
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafePgMigrations
4
+ module UselessStatementsLogger
5
+ def self.warn_useless(action, link = nil, *args)
6
+ SafePgMigrations.say "/!\\ No need to explicitly use #{action}, safe-pg-migrations does it for you", *args
7
+ SafePgMigrations.say "\t see #{link} for more details", *args if link
8
+ end
9
+
10
+ def add_index(*, **options)
11
+ warn_for_index(**options)
12
+ super
13
+ end
14
+
15
+ def remove_index(table_name, options = {})
16
+ warn_for_index(options) if options.is_a? Hash
17
+ super
18
+ end
19
+
20
+ def add_foreign_key(*, **options)
21
+ if options[:validate] == false
22
+ UselessStatementsLogger.warn_useless '`validate: :false`', 'https://github.com/doctolib/safe-pg-migrations#safe_add_foreign_key'
23
+ end
24
+ super
25
+ end
26
+
27
+ def warn_for_index(**options)
28
+ return unless options[:algorithm] == :concurrently
29
+
30
+ UselessStatementsLogger.warn_useless '`algorithm: :concurrently`', 'https://github.com/doctolib/safe-pg-migrations#safe_add_remove_index'
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SafePgMigrations
4
- VERSION = '1.1.0'
4
+ VERSION = '1.2.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: safe-pg-migrations
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matthieu Prat
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-11-03 00:00:00.000000000 Z
12
+ date: 2020-11-30 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -166,6 +166,7 @@ files:
166
166
  - lib/safe-pg-migrations/plugins/idem_potent_statements.rb
167
167
  - lib/safe-pg-migrations/plugins/statement_insurer.rb
168
168
  - lib/safe-pg-migrations/plugins/statement_retrier.rb
169
+ - lib/safe-pg-migrations/plugins/useless_statements_logger.rb
169
170
  - lib/safe-pg-migrations/plugins/verbose_sql_logger.rb
170
171
  - lib/safe-pg-migrations/railtie.rb
171
172
  - lib/safe-pg-migrations/version.rb