safe-pg-migrations 0.0.3 → 1.0.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
  SHA1:
3
- metadata.gz: 064f4f95d22160ab489fbe40a4af338110144c74
4
- data.tar.gz: 6c7e864e8b88251d9c3defb93c986af186e19a97
3
+ metadata.gz: d3e47cc5a8440b59b3e25dc7a5e99a9212c4709c
4
+ data.tar.gz: 7347fdd053c1edd34b3ec58d9021b126ec794eb3
5
5
  SHA512:
6
- metadata.gz: 68157b6bbaf2f8b5a363f0796e7e98a5cfa19ccd6e6bdc2981e4b523dbc24b63fda023807c0d13964da544f1323d186ce180265e0f3f9e66dd6faa193d601baa
7
- data.tar.gz: 7b0cc78b1552683538548f6ab57b05154f80009f022073c4c993d4226f1cda6f91576f0a090493b7f5f4dc61073ad6aaf1d63100e78ec984ee25c89d2b7770f3
6
+ metadata.gz: 74058e22a0e55ae44e29b8e74096bf0b4bf6b7c83a7fafce8c6aa3595c50bafec2d93c6ce502d304f89b44e236a3f3770fac91ff7e9bb449a3289cca0a06ab89
7
+ data.tar.gz: 7a38389555cb2cd8c0271882389aed8e27b1d22ef7cb16976c729041ef315803ad7de0a71400f478d50c7c741e1b10a5a37109940de5a5e7a8809800eecdff25
data/README.md CHANGED
@@ -28,7 +28,7 @@ class AddAdminToUsers < ActiveRecord::Migration[5.2]
28
28
  end
29
29
  ```
30
30
 
31
- If the `users` table is large, running this migration on a live Postgres database will likely cause downtime. **Safe PG Migrations** hooks into Active Record so that the following gets executed instead:
31
+ If the `users` table is large, running this migration on a live Postgres 9 database will likely cause downtime. **Safe PG Migrations** hooks into Active Record so that the following gets executed instead:
32
32
 
33
33
  ```rb
34
34
  class AddAdminToUsers < ActiveRecord::Migration[5.2]
@@ -69,19 +69,19 @@ Active Record means developers don't have to be proficient in SQL to interact wi
69
69
 
70
70
  ### Lock timeout
71
71
 
72
- Most DDL operations (e.g. adding a column, removing a column or adding a default value to a column) take an `ACCESS EXCLUSIVE` lock on the table they are altering. While these operations wait to acquire their lock, other statements are blocked. Before running a migration, **Safe PG Migrations** sets a short lock timeout so that statements are not blocked for too long.
72
+ Most DDL operations (e.g. adding a column, removing a column or adding a default value to a column) take an `ACCESS EXCLUSIVE` lock on the table they are altering. While these operations wait to acquire their lock, other statements are blocked. Before running a migration, **Safe PG Migrations** sets a short lock timeout (default to 5 seconds) so that statements are not blocked for too long.
73
73
 
74
74
  See [PostgreSQL Alter Table and Long Transactions](http://www.joshuakehn.com/2017/9/9/postgresql-alter-table-and-long-transactions.html) and [Migrations and Long Transactions](https://www.fin.com/post/2018/1/migrations-and-long-transactions) for detailed explanations of the matter.
75
75
 
76
76
  ### Statement timeout
77
77
 
78
- Adding a foreign key or a not-null constraint can take a lot of time on a large table. The problem is that those operations take `ACCESS EXCLUSIVE` locks. We clearly don't want them to hold these locks for too long. Thus, **Safe PG Migrations** runs them with a short statement timeout.
78
+ Adding a foreign key or a not-null constraint can take a lot of time on a large table. The problem is that those operations take `ACCESS EXCLUSIVE` locks. We clearly don't want them to hold these locks for too long. Thus, **Safe PG Migrations** runs them with a short statement timeout (default to 5 seconds).
79
79
 
80
80
  See [Zero-downtime Postgres migrations - the hard parts](https://gocardless.com/blog/zero-downtime-postgres-migrations-the-hard-parts/) for a detailed explanation on the subject.
81
81
 
82
82
  ### Prevent wrapping migrations in transaction
83
83
 
84
- When **Safe PG Migrations** is enabled (which is the case by default if `Rails.env.production?` is true), migrations are not wrapped in a transaction. This is for several reasons:
84
+ When **Safe PG Migrations** is used, migrations are not wrapped in a transaction. This is for several reasons:
85
85
 
86
86
  - We want to release locks as soon as possible.
87
87
  - In order to be able to retry statements that have failed because of a lock timeout, we have to be outside a transaction.
@@ -91,6 +91,8 @@ Note that if a migration fails, it won't be rollbacked. This can result in migra
91
91
 
92
92
  ### Safe `add_column`
93
93
 
94
+ #### Pre Postgres 11 behavior
95
+
94
96
  Adding a column with a default value and a not-null constraint is [dangerous](https://wework.github.io/data/2015/11/05/add-columns-with-default-values-to-large-tables-in-rails-postgres/).
95
97
 
96
98
  **Safe PG Migrations** makes it safe by:
@@ -102,18 +104,95 @@ Adding a column with a default value and a not-null constraint is [dangerous](ht
102
104
 
103
105
  Note: the addition of the not null constraint may timeout. In that case, you may want to add the not-null constraint as initially not valid and validate it in a separate statement. See [Adding a not-null constraint on Postgres with minimal locking](https://medium.com/doctolib-engineering/adding-a-not-null-constraint-on-pg-faster-with-minimal-locking-38b2c00c4d1c).
104
106
 
107
+ #### Postgres 11 behavior
108
+
109
+ **Safe PG Migrations** gracefully handle the upgrade to PG11 by **not** backfilling default value for existing rows, as the [database engine is now natively handling it](https://www.postgresql.org/docs/11/ddl-alter.html#DDL-ALTER-ADDING-A-COLUMN).
110
+
111
+ Beware though, when adding a volatile default value:
112
+ ```ruby
113
+ add_column :users, :created_at, default: 'clock_timestamp()'
114
+ ```
115
+ PG will still needs to update every row of the table, and will most likely statement timeout for big table. In this case, your best bet is to add the column without default, set the default, and backfill existing rows.
116
+
105
117
  ### Concurrent indexes
106
118
 
107
119
  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.
108
120
 
121
+ As `CREATE INDEX CONCURRENTLY` and `DROP INDEX CONCURRENTLY` are non-blocking operations (ie: read/write operations on the table are still possible), **Safe PG Migrations** sets a lock timeout to 30 seconds for those 2 specific statements.
122
+
123
+ If you still get lock timeout while adding / removing indexes, it might be for one of those reasons:
124
+
125
+ - Long-running queries are active on the table. To create / remove an index, PG needs to wait for the queries that are actually running to finish before starting the index creation / removal. The blocking activity logger might help you to pinpoint the culprit queries.
126
+ - A vacuum / autovacuum is running on the table, holding a ShareUpdateExclusiveLock, you are most likely out of luck for the current migration, but you may try to [optimize your autovacuums settings](https://www.percona.com/blog/2018/08/10/tuning-autovacuum-in-postgresql-and-autovacuum-internals/).
127
+
128
+
109
129
  ### Retry after lock timeout
110
130
 
111
- When a statement fails with a lock timeout, **Safe PG Migrations** retries them (5 times max).
131
+ When a statement fails with a lock timeout, **Safe PG Migrations** retries it (5 times max) [list of retryable statments](https://github.com/doctolib/safe-pg-migrations/blob/66933256252b6bbf12e404b829a361dbba30e684/lib/safe-pg-migrations/plugins/statement_retrier.rb#L5)
112
132
 
113
133
  ### Blocking activity logging
114
134
 
115
135
  If a statement fails with a lock timeout, **Safe PG Migrations** will try to tell you what was the blocking statement.
116
136
 
137
+ ### Verbose SQL logging
138
+
139
+ For any operation, **Safe PG Migrations** can output the performed SQL queries. This feature is enabled by default in a production Rails environment. If you want to explicit enable it, for example in your development environment you can use:
140
+ ```bash
141
+ export SAFE_PG_MIGRATIONS_VERBOSE=1
142
+ ```
143
+
144
+ Instead of the traditional output:
145
+ ```ruby
146
+ add_index :users, :age
147
+
148
+ == 20191215132355 SampleIndex: migrating ======================================
149
+ -- add_index(:users, :age)
150
+ -> add_index("users", :age, {:algorithm=>:concurrently})
151
+ -> 0.0175s
152
+ == 20191215132355 SampleIndex: migrated (0.0200s) =============================
153
+ ```
154
+ **Sage PG Migrations** will output the following logs:
155
+ ```ruby
156
+ add_index :users, :age
157
+
158
+ == 20191215132355 SampleIndex: migrating ======================================
159
+ (0.3ms) SHOW lock_timeout
160
+ (0.3ms) SET lock_timeout TO '5s'
161
+ -- add_index(:users, :age)
162
+ -> add_index("users", :age, {:algorithm=>:concurrently})
163
+ (0.3ms) SHOW statement_timeout
164
+ (0.3ms) SET statement_timeout TO 0
165
+ (0.3ms) SHOW lock_timeout
166
+ (0.3ms) SET lock_timeout TO '30s'
167
+ (3.5ms) CREATE INDEX CONCURRENTLY "index_users_on_age" ON "users" ("age")
168
+ (0.3ms) SET lock_timeout TO '5s'
169
+ (0.2ms) SET statement_timeout TO '1min'
170
+ -> 0.0093s
171
+ (0.2ms) SET lock_timeout TO '0'
172
+ == 20191215132355 SampleIndex: migrated (0.0114s) =============================
173
+ ```
174
+ So you can actually check that the `CREATE INDEX` statement will be performed concurrently, without any statement timeout and with a lock timeout of 30 seconds.
175
+
176
+ *Nb: The `SHOW` statements are used by **Safe PG Migrations** to query settings for their original values in order to restore them after the work is done*
177
+
178
+ ## Configuration
179
+
180
+ **Safe PG Migrations** can be customized, here is an example of a Rails initializer (the values are the default ones):
181
+
182
+ ```ruby
183
+ SafePgMigrations.config.safe_timeout = 5.seconds # Lock and statement timeout used for all DDL operations except from CREATE / DROP INDEX
184
+
185
+ SafePgMigrations.config.index_lock_timeout = 30.seconds # Lock timeout used for CREATE / DROP INDEX
186
+
187
+ 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
188
+
189
+ SafePgMigrations.config.batch_size = 1000 # Size of the batches used for backfilling when adding a column with a default value pre-PG11
190
+
191
+ SafePgMigrations.config.retry_delay = 1.minute # Delay between retries for retryable statements
192
+
193
+ SafePgMigrations.config.max_tries = 5 # Number of retries before abortion of the migration
194
+ ```
195
+
117
196
  ## Runnings tests
118
197
 
119
198
  ```bash
@@ -142,11 +221,12 @@ Alternatives:
142
221
 
143
222
  Interesting reads:
144
223
 
145
- - https://www.citusdata.com/blog/2018/02/22/seven-tips-for-dealing-with-postgres-locks/
146
- - https://www.fin.com/post/2018/1/migrations-and-long-transactions
147
- - http://www.joshuakehn.com/2017/9/9/postgresql-alter-table-and-long-transactions.html
148
- - https://medium.com/doctolib-engineering/adding-a-not-null-constraint-on-pg-faster-with-minimal-locking-38b2c00c4d1c
149
- - https://wework.github.io/data/2015/11/05/add-columns-with-default-values-to-large-tables-in-rails-postgres/
150
- - https://pedro.herokuapp.com/past/2011/7/13/rails_migrations_with_no_downtime/
151
- - https://www.braintreepayments.com/blog/safe-operations-for-high-volume-postgresql/
152
- - https://blog.codeship.com/rails-migrations-zero-downtime/
224
+ - [When Postgres blocks: 7 tips for dealing with locks](https://www.citusdata.com/blog/2018/02/22/seven-tips-for-dealing-with-postgres-locks/)
225
+ - [Migrations and Long Transactions in Postgres
226
+ ](https://www.fin.com/post/2018/1/migrations-and-long-transactions)
227
+ - [PostgreSQL Alter Table and Long Transactions](http://www.joshuakehn.com/2017/9/9/postgresql-alter-table-and-long-transactions.html)
228
+ - [Adding a NOT NULL CONSTRAINT on PG Faster with Minimal Locking](https://medium.com/doctolib-engineering/adding-a-not-null-constraint-on-pg-faster-with-minimal-locking-38b2c00c4d1c)
229
+ - [Adding columns with default values to really large tables in Postgres + Rails](https://wework.github.io/data/2015/11/05/add-columns-with-default-values-to-large-tables-in-rails-postgres/)
230
+ - [Rails migrations with no downtime](https://pedro.herokuapp.com/past/2011/7/13/rails_migrations_with_no_downtime/)
231
+ - [Safe Operations For High Volume PostgreSQL](https://www.braintreepayments.com/blog/safe-operations-for-high-volume-postgresql/)
232
+ - [Rails Migrations with Zero Downtime](https://blog.codeship.com/rails-migrations-zero-downtime/)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'safe-pg-migrations/configuration'
4
+ require 'safe-pg-migrations/plugins/verbose_sql_logger'
4
5
  require 'safe-pg-migrations/plugins/blocking_activity_logger'
5
6
  require 'safe-pg-migrations/plugins/statement_insurer'
6
7
  require 'safe-pg-migrations/plugins/statement_retrier'
@@ -16,17 +17,24 @@ module SafePgMigrations
16
17
  ].freeze
17
18
 
18
19
  class << self
19
- attr_reader :current_migration
20
- attr_accessor :enabled
20
+ attr_reader :current_migration, :pg_version_num
21
21
 
22
22
  def setup_and_teardown(migration, connection)
23
+ @pg_version_num = get_pg_version_num(connection)
23
24
  @alternate_connection = nil
24
25
  @current_migration = migration
26
+ stdout_sql_logger = VerboseSqlLogger.new.setup if verbose?
25
27
  PLUGINS.each { |plugin| connection.extend(plugin) }
26
- connection.with_setting(:lock_timeout, SafePgMigrations.config.safe_timeout) { yield }
28
+
29
+ connection.with_setting(:lock_timeout, SafePgMigrations.config.pg_safe_timeout) { yield }
27
30
  ensure
28
31
  close_alternate_connection
29
32
  @current_migration = nil
33
+ stdout_sql_logger&.teardown
34
+ end
35
+
36
+ def get_pg_version_num(connection)
37
+ connection.query_value('SHOW server_version_num').to_i
30
38
  end
31
39
 
32
40
  def alternate_connection
@@ -50,9 +58,8 @@ module SafePgMigrations
50
58
  say "#{method}(#{args.map(&:inspect) * ', '})", true
51
59
  end
52
60
 
53
- def enabled?
54
- return ENV['SAFE_PG_MIGRATIONS'] == '1' if ENV['SAFE_PG_MIGRATIONS']
55
- return enabled unless enabled.nil?
61
+ def verbose?
62
+ return ENV['SAFE_PG_MIGRATIONS_VERBOSE'] == '1' if ENV['SAFE_PG_MIGRATIONS_VERBOSE']
56
63
  return Rails.env.production? if defined?(Rails)
57
64
 
58
65
  false
@@ -71,7 +78,7 @@ module SafePgMigrations
71
78
  end
72
79
 
73
80
  def disable_ddl_transaction
74
- SafePgMigrations.enabled? || super
81
+ true
75
82
  end
76
83
 
77
84
  SAFE_METHODS = %i[execute add_column add_index add_reference add_belongs_to change_column_null].freeze
@@ -5,17 +5,32 @@ require 'active_support/core_ext/numeric/time'
5
5
  module SafePgMigrations
6
6
  class Configuration
7
7
  attr_accessor :safe_timeout
8
- attr_accessor :blocking_activity_logger_delay # Must be close to but smaller than safe_timeout.
8
+ attr_accessor :index_lock_timeout
9
+ attr_accessor :blocking_activity_logger_margin
9
10
  attr_accessor :batch_size
10
11
  attr_accessor :retry_delay
11
12
  attr_accessor :max_tries
12
13
 
13
14
  def initialize
14
- self.safe_timeout = '5s'
15
- self.blocking_activity_logger_delay = 4.seconds
15
+ self.safe_timeout = 5.seconds
16
+ self.index_lock_timeout = 30.seconds
17
+ self.blocking_activity_logger_margin = 1.second
16
18
  self.batch_size = 1000
17
- self.retry_delay = 2.minutes
19
+ self.retry_delay = 1.minute
18
20
  self.max_tries = 5
19
21
  end
22
+
23
+ def pg_safe_timeout
24
+ pg_duration(safe_timeout)
25
+ end
26
+
27
+ def pg_index_lock_timeout
28
+ pg_duration(index_lock_timeout)
29
+ end
30
+
31
+ def pg_duration(duration)
32
+ value, unit = duration.integer? ? [duration, 's'] : [(duration * 1000).to_i, 'ms']
33
+ "#{value}#{unit}"
34
+ end
20
35
  end
21
36
  end
@@ -29,16 +29,27 @@ module SafePgMigrations
29
29
  change_column_null create_table add_index remove_index
30
30
  ].each do |method|
31
31
  define_method method do |*args, &block|
32
- log_blocking_queries { super(*args, &block) }
32
+ log_blocking_queries(method) { super(*args, &block) }
33
33
  end
34
34
  end
35
35
 
36
36
  private
37
37
 
38
- def log_blocking_queries
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
48
+
49
+ def log_blocking_queries(method)
39
50
  blocking_queries_retriever_thread =
40
51
  Thread.new do
41
- sleep SafePgMigrations.config.blocking_activity_logger_delay
52
+ sleep delay_before_logging(method)
42
53
  SafePgMigrations.alternate_connection.query_values(SELECT_BLOCKING_QUERIES_SQL % raw_connection.backend_pid)
43
54
  end
44
55
 
@@ -65,6 +76,11 @@ module SafePgMigrations
65
76
  )
66
77
  SafePgMigrations.say '', true
67
78
  queries.each { |query| SafePgMigrations.say " #{query}", true }
79
+ SafePgMigrations.say(
80
+ 'Beware, some of those queries might run in a transaction. In this case the locking query might be '\
81
+ 'located elsewhere in the transaction',
82
+ true
83
+ )
68
84
  SafePgMigrations.say '', true
69
85
  end
70
86
 
@@ -2,14 +2,18 @@
2
2
 
3
3
  module SafePgMigrations
4
4
  module StatementInsurer
5
+ PG_11_VERSION_NUM = 110_000
6
+
5
7
  %i[change_column_null add_foreign_key create_table].each do |method|
6
8
  define_method method do |*args, &block|
7
- with_setting(:statement_timeout, SafePgMigrations.config.safe_timeout) { super(*args, &block) }
9
+ with_setting(:statement_timeout, SafePgMigrations.config.pg_safe_timeout) { super(*args, &block) }
8
10
  end
9
11
  end
10
12
 
11
13
  def add_column(table_name, column_name, type, **options)
12
- default = options.delete(:default)
14
+ need_default_value_backfill = SafePgMigrations.pg_version_num < PG_11_VERSION_NUM
15
+
16
+ default = options.delete(:default) if need_default_value_backfill
13
17
  null = options.delete(:null)
14
18
 
15
19
  if !default.nil? || null == false
@@ -18,7 +22,7 @@ module SafePgMigrations
18
22
 
19
23
  super
20
24
 
21
- unless default.nil?
25
+ if need_default_value_backfill && !default.nil?
22
26
  SafePgMigrations.say_method_call(:change_column_default, table_name, column_name, default)
23
27
  change_column_default(table_name, column_name, default)
24
28
 
@@ -33,20 +37,18 @@ module SafePgMigrations
33
37
  end
34
38
 
35
39
  def add_index(table_name, column_name, **options)
36
- if SafePgMigrations.enabled?
37
- options[:algorithm] = :concurrently
38
- SafePgMigrations.say_method_call(:add_index, table_name, column_name, **options)
39
- end
40
- without_statement_timeout { super }
40
+ options[:algorithm] = :concurrently
41
+ SafePgMigrations.say_method_call(:add_index, table_name, column_name, **options)
42
+
43
+ with_index_timeouts { super }
41
44
  end
42
45
 
43
46
  def remove_index(table_name, options = {})
44
47
  options = { column: options } unless options.is_a?(Hash)
45
- if SafePgMigrations.enabled?
46
- options[:algorithm] = :concurrently
47
- SafePgMigrations.say_method_call(:remove_index, table_name, **options)
48
- end
49
- without_statement_timeout { super }
48
+ options[:algorithm] = :concurrently
49
+ SafePgMigrations.say_method_call(:remove_index, table_name, **options)
50
+
51
+ with_index_timeouts { super }
50
52
  end
51
53
 
52
54
  def backfill_column_default(table_name, column_name)
@@ -86,5 +88,13 @@ module SafePgMigrations
86
88
  def without_statement_timeout
87
89
  with_setting(:statement_timeout, 0) { yield }
88
90
  end
91
+
92
+ def with_index_timeouts
93
+ without_statement_timeout do
94
+ with_setting(:lock_timeout, SafePgMigrations.config.pg_index_lock_timeout) do
95
+ yield
96
+ end
97
+ end
98
+ end
89
99
  end
90
100
  end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SafePgMigrations
4
+ class VerboseSqlLogger
5
+ def setup
6
+ @activerecord_logger_was = ActiveRecord::Base.logger
7
+ @verbose_query_logs_was = ActiveRecord::Base.verbose_query_logs
8
+ @colorize_logging_was = ActiveRecord::LogSubscriber.colorize_logging
9
+
10
+ disable_marginalia if defined?(Marginalia)
11
+
12
+ stdout_logger = Logger.new($stdout, formatter: ->(_severity, _time, _progname, query) { "#{query}\n" })
13
+ ActiveRecord::Base.logger = stdout_logger
14
+ ActiveRecord::LogSubscriber.colorize_logging = colorize_logging?
15
+ # Do not output caller method, we know it is coming from the migration
16
+ ActiveRecord::Base.verbose_query_logs = false
17
+ self
18
+ end
19
+
20
+ def teardown
21
+ ActiveRecord::Base.verbose_query_logs = @verbose_query_logs_was
22
+ ActiveRecord::LogSubscriber.colorize_logging = @colorize_logging_was
23
+ ActiveRecord::Base.logger = @activerecord_logger_was
24
+ enable_marginalia if defined?(Marginalia)
25
+ end
26
+
27
+ private
28
+
29
+ def colorize_logging?
30
+ defined?(Rails) && Rails.env.development?
31
+ end
32
+
33
+ # Marginalia annotations will most likely pollute the output
34
+ def disable_marginalia
35
+ @marginalia_components_were = Marginalia::Comment.components
36
+ Marginalia::Comment.components = []
37
+ end
38
+
39
+ def enable_marginalia
40
+ Marginalia::Comment.components = @marginalia_components_were
41
+ end
42
+ end
43
+ end
@@ -4,7 +4,7 @@ require 'safe-pg-migrations/base'
4
4
 
5
5
  module SafePgMigrations
6
6
  class Railtie < Rails::Railtie
7
- initializer 'sage_pg_migrations.insert_into_active_record' do
7
+ initializer 'safe_pg_migrations.insert_into_active_record' do
8
8
  ActiveSupport.on_load :active_record do
9
9
  ActiveRecord::Migration.prepend(SafePgMigrations::Migration)
10
10
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SafePgMigrations
4
- VERSION = '0.0.3'
4
+ VERSION = '1.0.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: 0.0.3
4
+ version: 1.0.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: 2018-11-22 00:00:00.000000000 Z
12
+ date: 2019-12-17 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/verbose_sql_logger.rb
169
170
  - lib/safe-pg-migrations/railtie.rb
170
171
  - lib/safe-pg-migrations/version.rb
171
172
  homepage: https://github.com/doctolib/safe-pg-migrations