pg_online_schema_change 0.9.6 → 0.9.8

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: 839fe414842291d9118a4115a56e06f1e0308ab3b7a740cafd9994314ded2d76
4
- data.tar.gz: 22e9639093bf386f8a1e554e15e8d83df5e3c6de340c372f181fa3b280aee98d
3
+ metadata.gz: aed221e19c7fe40cf5a34844625c78b02c56fe13aff7f65183a1cff717185ad7
4
+ data.tar.gz: 81d3a564f43ebf08a7cac37fddd9a035d253e6dcc8c1c5d178c3c247e6357bc0
5
5
  SHA512:
6
- metadata.gz: a160d19b3f1cb6655f70e0481b3293110deaee04d7a017ff9fe0c2b4d6100fd7d475520092db1b3bb5e068df5cb70243a00397a5cb1c2c259f2fe5b776b7e405
7
- data.tar.gz: 9e4543fde48359fdc12046ff019420aafe51732f5cd1332285b3762fac3dcb198acc7be4f5fc6f6f90b910532758eb4a5752a8c9835d1f394774e6f77eda3898
6
+ metadata.gz: 5a4cfb3e17d007803cb84cf0686cadcfd0e7f7734ad20f6f5fa8b71a5198a24569a390ef3173f7bb518e345c7c4931c10812c053535d1df20ba774382c239f80
7
+ data.tar.gz: f82f723f5cac41a77c6adec6ddaa83c0377b172b3b283c05f1b81e7538c8427e182cdbac1db53e9358b39842685403b3a6e85cdb5a0371b6bffeef608e67a690
data/.rubocop.yml CHANGED
@@ -215,6 +215,9 @@ RSpec/DescribeClass:
215
215
  RSpec/DescribedClass:
216
216
  Enabled: false
217
217
 
218
+ Rspec/FilePath:
219
+ Enabled: false
220
+
218
221
  RSpec/SpecFilePathFormat:
219
222
  Enabled: false
220
223
 
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 3.1.3
1
+ 3.3.0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## [0.9.7] - 2024-01-15
2
+
3
+ - Introduce the ability to show estimated progress of copy - #146
4
+
5
+ ## [0.9.6] - 2023-11-04
6
+
7
+ - Fix and add links to caveats section in #130
8
+ - Refresh views across all schemas post swap in #134
9
+
1
10
  ## [0.9.5] - 2023-10-15
2
11
 
3
12
  - Validate one constraint at a time in #124
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- pg_online_schema_change (0.9.6)
4
+ pg_online_schema_change (0.9.8)
5
5
  ougai (~> 2.0.0)
6
6
  pg (>= 1.3.2, < 1.6.0)
7
7
  pg_query (>= 2.1.3, < 4.3.0)
@@ -10,22 +10,25 @@ PATH
10
10
  GEM
11
11
  remote: https://rubygems.org/
12
12
  specs:
13
+ abbrev (0.1.2)
13
14
  ast (2.4.2)
15
+ bigdecimal (3.1.5)
14
16
  coderay (1.1.3)
15
17
  diff-lcs (1.5.0)
16
- google-protobuf (3.25.0-arm64-darwin)
17
- haml (6.1.1)
18
+ google-protobuf (3.25.2)
19
+ haml (6.3.0)
18
20
  temple (>= 0.8.2)
19
21
  thor
20
22
  tilt
21
- json (2.6.3)
23
+ json (2.7.1)
22
24
  language_server-protocol (3.17.0.3)
23
25
  method_source (1.0.0)
24
- oj (3.16.1)
26
+ oj (3.16.3)
27
+ bigdecimal (>= 3.0)
25
28
  ougai (2.0.0)
26
29
  oj (~> 3.10)
27
- parallel (1.23.0)
28
- parser (3.2.2.4)
30
+ parallel (1.24.0)
31
+ parser (3.3.0.4)
29
32
  ast (~> 2.4.1)
30
33
  racc
31
34
  pg (1.5.4)
@@ -35,11 +38,12 @@ GEM
35
38
  pry (0.14.2)
36
39
  coderay (~> 1.1)
37
40
  method_source (~> 1.0)
38
- racc (1.7.1)
41
+ racc (1.7.3)
39
42
  rainbow (3.1.1)
40
43
  rake (13.1.0)
41
- rbs (3.1.0)
42
- regexp_parser (2.8.2)
44
+ rbs (3.4.1)
45
+ abbrev
46
+ regexp_parser (2.9.0)
43
47
  rexml (3.2.6)
44
48
  rspec (3.12.0)
45
49
  rspec-core (~> 3.12.0)
@@ -50,36 +54,36 @@ GEM
50
54
  rspec-expectations (3.12.3)
51
55
  diff-lcs (>= 1.2.0, < 2.0)
52
56
  rspec-support (~> 3.12.0)
53
- rspec-mocks (3.12.5)
57
+ rspec-mocks (3.12.6)
54
58
  diff-lcs (>= 1.2.0, < 2.0)
55
59
  rspec-support (~> 3.12.0)
56
- rspec-support (3.12.0)
57
- rubocop (1.57.2)
60
+ rspec-support (3.12.1)
61
+ rubocop (1.60.0)
58
62
  json (~> 2.3)
59
63
  language_server-protocol (>= 3.17.0)
60
64
  parallel (~> 1.10)
61
- parser (>= 3.2.2.4)
65
+ parser (>= 3.3.0.2)
62
66
  rainbow (>= 2.2.2, < 4.0)
63
67
  regexp_parser (>= 1.8, < 3.0)
64
68
  rexml (>= 3.2.5, < 4.0)
65
- rubocop-ast (>= 1.28.1, < 2.0)
69
+ rubocop-ast (>= 1.30.0, < 2.0)
66
70
  ruby-progressbar (~> 1.7)
67
71
  unicode-display_width (>= 2.4.0, < 3.0)
68
72
  rubocop-ast (1.30.0)
69
73
  parser (>= 3.2.1.0)
70
- rubocop-capybara (2.18.0)
74
+ rubocop-capybara (2.20.0)
75
+ rubocop (~> 1.41)
76
+ rubocop-factory_bot (2.25.1)
71
77
  rubocop (~> 1.41)
72
- rubocop-factory_bot (2.23.1)
73
- rubocop (~> 1.33)
74
78
  rubocop-packaging (0.5.2)
75
79
  rubocop (>= 1.33, < 2.0)
76
- rubocop-performance (1.19.1)
77
- rubocop (>= 1.7.0, < 2.0)
78
- rubocop-ast (>= 0.4.0)
80
+ rubocop-performance (1.20.2)
81
+ rubocop (>= 1.48.1, < 2.0)
82
+ rubocop-ast (>= 1.30.0, < 2.0)
79
83
  rubocop-rake (0.6.0)
80
84
  rubocop (~> 1.0)
81
- rubocop-rspec (2.24.0)
82
- rubocop (~> 1.33)
85
+ rubocop-rspec (2.26.1)
86
+ rubocop (~> 1.40)
83
87
  rubocop-capybara (~> 2.17)
84
88
  rubocop-factory_bot (~> 2.22)
85
89
  ruby-progressbar (1.13.0)
@@ -93,9 +97,9 @@ GEM
93
97
  prettier_print
94
98
  rbs
95
99
  syntax_tree (>= 2.0.1)
96
- temple (0.10.0)
100
+ temple (0.10.3)
97
101
  thor (1.3.0)
98
- tilt (2.1.0)
102
+ tilt (2.3.0)
99
103
  unicode-display_width (2.5.0)
100
104
 
101
105
  PLATFORMS
data/README.md CHANGED
@@ -107,9 +107,9 @@ print the version
107
107
  - `pg-osc` acquires minimal locks throughout the process (read more below on the [caveats](#few-things-to-keep-in-mind)).
108
108
  - Copies over indexes and Foreign keys.
109
109
  - Optionally drop or retain old tables in the end.
110
+ - Reduce bloat (since pg-osc creates a new table and drops the old one post swap).
110
111
  - Tune how slow or fast should replays be from the audit/log table ([Replaying larger workloads](#replaying-larger-workloads)).
111
112
  - Backfill old/new columns as data is copied from primary table to shadow table, and then perform the swap. [Example](#backfill-data)
112
- - **TBD**: Ability to reverse the change with no data loss. [tracking issue](https://github.com/shayonj/pg-osc/issues/14)
113
113
 
114
114
  ## Load test
115
115
 
@@ -67,5 +67,9 @@ module PgOnlineSchemaChange
67
67
 
68
68
  @copy_statement = File.binread(file_path)
69
69
  end
70
+
71
+ def checkout_connection
72
+ PG.connect(dbname: dbname, host: host, user: username, password: password, port: port)
73
+ end
70
74
  end
71
75
  end
@@ -5,6 +5,7 @@ require "securerandom"
5
5
  module PgOnlineSchemaChange
6
6
  class Orchestrate
7
7
  SWAP_STATEMENT_TIMEOUT = "5s"
8
+ TRACK_PROGRESS_INTERVAL = 60 # seconds
8
9
 
9
10
  extend Helper
10
11
 
@@ -58,6 +59,10 @@ module PgOnlineSchemaChange
58
59
 
59
60
  raise Error, "Parent table has no primary key, exiting..." if primary_key.nil?
60
61
 
62
+ logger.info("Performing some house keeping....")
63
+ run_analyze!
64
+ run_vacuum!
65
+
61
66
  setup_audit_table!
62
67
 
63
68
  setup_trigger!
@@ -159,6 +164,12 @@ module PgOnlineSchemaChange
159
164
  end
160
165
 
161
166
  def setup_shadow_table!
167
+ logger.info("Setting up shadow table", { shadow_table: shadow_table })
168
+ Query.run(
169
+ client.connection,
170
+ "SELECT create_table_all('#{client.table_name}', '#{shadow_table}');",
171
+ )
172
+
162
173
  # re-uses transaction with serializable
163
174
  # This ensures that all queries from here till copy_data run with serializable.
164
175
  # This is to to ensure that once the trigger is added to the primay table
@@ -168,13 +179,6 @@ module PgOnlineSchemaChange
168
179
  # adding the trigger, till the copy ends, since they all happen in the
169
180
  # same serializable transaction.
170
181
  Query.run(client.connection, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", true)
171
- logger.info("Setting up shadow table", { shadow_table: shadow_table })
172
-
173
- Query.run(
174
- client.connection,
175
- "SELECT create_table_all('#{client.table_name}', '#{shadow_table}');",
176
- true,
177
- )
178
182
 
179
183
  # update serials
180
184
  Query.run(
@@ -208,19 +212,44 @@ module PgOnlineSchemaChange
208
212
  )
209
213
  Query.run(client.connection, "DELETE FROM #{audit_table}", true)
210
214
 
211
- logger.info(
212
- "Copying contents..",
213
- { shadow_table: shadow_table, parent_table: client.table_name },
214
- )
215
215
  if client.copy_statement
216
216
  query = format(client.copy_statement, shadow_table: shadow_table)
217
217
  return Query.run(client.connection, query, true)
218
218
  end
219
219
 
220
+ logger.info(
221
+ "Copying contents..",
222
+ { shadow_table: shadow_table, parent_table: client.table_name },
223
+ )
224
+
225
+ @copy_finished = false
226
+ log_progress
227
+
220
228
  sql = Query.copy_data_statement(client, shadow_table, true)
221
229
  Query.run(client.connection, sql, true)
222
230
  ensure
223
231
  Query.run(client.connection, "COMMIT;") # commit the serializable transaction
232
+ @copy_finished = true
233
+ end
234
+
235
+ def log_progress
236
+ new_connection = client.checkout_connection
237
+ source_table_size = Query.get_table_size(new_connection, client.schema, client.table_name)
238
+
239
+ Thread.new do
240
+ loop do
241
+ break if @copy_finished
242
+
243
+ shadow_table_size = Query.get_table_size(new_connection, client.schema, shadow_table)
244
+ progress = (shadow_table_size.to_f / source_table_size) * 100
245
+ logger.info("Estimated copy progress: #{progress.round(2)}% complete")
246
+
247
+ break if @copy_finished || progress >= 100
248
+ sleep(TRACK_PROGRESS_INTERVAL) unless ENV["CI"]
249
+ rescue StandardError => e
250
+ logger.info("Reporting progress failed: #{e.message}")
251
+ end
252
+ end
224
253
  end
225
254
 
226
255
  def replay_and_swap!
@@ -272,7 +301,13 @@ module PgOnlineSchemaChange
272
301
  def run_analyze!
273
302
  logger.info("Performing ANALYZE!")
274
303
 
275
- Query.run(client.connection, "ANALYZE VERBOSE #{client.table_name};")
304
+ client.connection.async_exec("ANALYZE VERBOSE #{client.schema}.#{client.table_name};")
305
+ end
306
+
307
+ def run_vacuum!
308
+ logger.info("Performing VACUUM!")
309
+
310
+ client.connection.async_exec("VACUUM VERBOSE #{client.schema}.#{client.table_name};")
276
311
  end
277
312
 
278
313
  def validate_constraints!
@@ -66,7 +66,7 @@ module PgOnlineSchemaChange
66
66
  end
67
67
  end
68
68
 
69
- def run(connection, query, reuse_trasaction = false, &block)
69
+ def run(connection, query, reuse_trasaction = false, &block) # rubocop:disable Style/ArgumentsForwarding
70
70
  if [PG::PQTRANS_INERROR, PG::PQTRANS_UNKNOWN].include?(connection.transaction_status)
71
71
  connection.cancel
72
72
  end
@@ -75,7 +75,7 @@ module PgOnlineSchemaChange
75
75
 
76
76
  connection.async_exec("BEGIN;")
77
77
 
78
- result = connection.async_exec(query, &block)
78
+ result = connection.async_exec(query, &block) # rubocop:disable Style/ArgumentsForwarding
79
79
  rescue Exception # rubocop:disable Lint/RescueException
80
80
  connection.cancel if connection.transaction_status != PG::PQTRANS_IDLE
81
81
  connection.block
@@ -428,6 +428,15 @@ module PgOnlineSchemaChange
428
428
  SELECT setval((select pg_get_serial_sequence('#{shadow_table}', '#{primary_key}')), (SELECT max(#{primary_key}) FROM #{table}));
429
429
  SQL
430
430
  end
431
+
432
+ def get_table_size(connection, schema, table_name)
433
+ size_query = "SELECT pg_table_size('#{schema}.#{table_name}');"
434
+ result = run(connection, size_query).first
435
+ result["pg_table_size"].to_i
436
+ rescue StandardError => e
437
+ logger.error("Error getting table size: #{e.message}")
438
+ 0
439
+ end
431
440
  end
432
441
  end
433
442
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module PgOnlineSchemaChange
4
- VERSION = "0.9.6"
4
+ VERSION = "0.9.8"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_online_schema_change
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.6
4
+ version: 0.9.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shayon Mukherjee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-04 00:00:00.000000000 Z
11
+ date: 2024-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ougai
@@ -311,14 +311,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
311
311
  requirements:
312
312
  - - ">="
313
313
  - !ruby/object:Gem::Version
314
- version: 2.7.0
314
+ version: 3.0.0
315
315
  required_rubygems_version: !ruby/object:Gem::Requirement
316
316
  requirements:
317
317
  - - ">="
318
318
  - !ruby/object:Gem::Version
319
319
  version: '0'
320
320
  requirements: []
321
- rubygems_version: 3.3.26
321
+ rubygems_version: 3.5.3
322
322
  signing_key:
323
323
  specification_version: 4
324
324
  summary: Easy CLI tool for making non-blocking zero downtime schema changes in PostgreSQL