pg_online_schema_change 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +4 -1
- data/lib/pg_online_schema_change/orchestrate.rb +33 -16
- data/lib/pg_online_schema_change/query.rb +6 -6
- data/lib/pg_online_schema_change/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3d69a5ecadf6f0c3b3c27bb2e16e163cfbc6526c4c83b5cbdf4ebef73be62cf9
|
4
|
+
data.tar.gz: 5a8ac6b5491692d4680f40637550392ebe5e8a206e7e3b1729967a379231ee8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b8960dae33e3cf5bace38cb45cacaa555d16433b15703ffda2f2338b5076e716c23271d5f08d77f033cfbe9c6f2ea450e764a8ce934bb3ec9bfd9327cf89735
|
7
|
+
data.tar.gz: f003a55abb25ad2bcecf5a4e3b2358bfac4b3c0c55c401f7500a8d92e2b5bd30ebb4891d934cec52a1449e65a4fd9d177a9f87a2493d72b3f949ef47493943e8
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# pg-online-schema-change / pg-osc
|
2
2
|
[![CircleCI](https://circleci.com/gh/shayonj/pg-online-schema-change/tree/main.svg?style=shield)](https://circleci.com/gh/shayonj/pg-online-schema-change/tree/main)
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/pg_online_schema_change.svg)](https://badge.fury.io/rb/pg_online_schema_change)
|
3
4
|
|
4
5
|
pg-online-schema-change (`pg-osc`) is a tool for making schema changes (any `ALTER` statements) in Postgres tables with minimal locks, thus helping achieve zero downtime schema changes against production workloads.
|
5
6
|
|
6
|
-
`pg-osc` uses the concept of shadow table to perform schema changes. At a high level, it
|
7
|
+
`pg-osc` uses the concept of shadow table to perform schema changes. At a high level, it creates a shadow table that looks structurally the same as the primary table, performs the schema change on the shadow table, copies contents from the primary table to the shadow table and swaps the table names in the end while preserving all changes to the primary table using triggers (via audit table).
|
7
8
|
|
8
9
|
`pg-osc` is inspired by the design and workings of tools like `pg_repack` and `pt-online-schema-change` (MySQL). Read more below on [how does it work](#how-does-it-work), [prominent features](#prominent-features), the [caveats](#caveats) and [examples](#examples)
|
9
10
|
|
@@ -33,6 +34,8 @@ Or install it yourself as:
|
|
33
34
|
## Usage
|
34
35
|
|
35
36
|
```
|
37
|
+
pg-online-schema-change help perform
|
38
|
+
|
36
39
|
Usage:
|
37
40
|
pg-online-schema-change perform -a, --alter-statement=ALTER_STATEMENT -d, --dbname=DBNAME -h, --host=HOST -p, --port=N -s, --schema=SCHEMA -u, --username=USERNAME -w, --password=PASSWORD
|
38
41
|
|
@@ -37,10 +37,10 @@ module PgOnlineSchemaChange
|
|
37
37
|
|
38
38
|
setup_audit_table!
|
39
39
|
setup_trigger!
|
40
|
-
setup_shadow_table!
|
41
|
-
disable_vacuum!
|
42
|
-
run_alter_statement!
|
43
|
-
copy_data!
|
40
|
+
setup_shadow_table! # re-uses transaction with serializable
|
41
|
+
disable_vacuum! # re-uses transaction with serializable
|
42
|
+
run_alter_statement! # re-uses transaction with serializable
|
43
|
+
copy_data! # re-uses transaction with serializable
|
44
44
|
run_analyze!
|
45
45
|
replay_and_swap!
|
46
46
|
run_analyze!
|
@@ -132,17 +132,27 @@ module PgOnlineSchemaChange
|
|
132
132
|
end
|
133
133
|
|
134
134
|
def setup_shadow_table!
|
135
|
+
# re-uses transaction with serializable
|
136
|
+
# This ensures that all queries from here till copy_data run with serializable.
|
137
|
+
# This is to to ensure that once the trigger is added to the primay table
|
138
|
+
# and contents being copied into the shadow, after a delete all on audit table,
|
139
|
+
# any replaying of rows that happen next from audit table do not contain
|
140
|
+
# any duplicates. We are ensuring there are no race conditions between
|
141
|
+
# adding the trigger, till the copy ends, since they all happen in the
|
142
|
+
# same serializable transaction.
|
143
|
+
Query.run(client.connection, "SET TRANSACTION ISOLATION LEVEL SERIALIZABLE", true)
|
135
144
|
logger.info("Setting up shadow table", { shadow_table: shadow_table })
|
136
145
|
|
137
|
-
Query.run(client.connection, "SELECT create_table_all('#{client.table}', '#{shadow_table}');")
|
146
|
+
Query.run(client.connection, "SELECT create_table_all('#{client.table}', '#{shadow_table}');", true)
|
138
147
|
|
139
148
|
# update serials
|
140
|
-
Query.run(client.connection, "SELECT fix_serial_sequence('#{client.table}', '#{shadow_table}');")
|
149
|
+
Query.run(client.connection, "SELECT fix_serial_sequence('#{client.table}', '#{shadow_table}');", true)
|
141
150
|
end
|
142
151
|
|
143
|
-
# Disabling vacuum to avoid any issues during the process
|
144
152
|
def disable_vacuum!
|
145
|
-
|
153
|
+
# re-uses transaction with serializable
|
154
|
+
# Disabling vacuum to avoid any issues during the process
|
155
|
+
result = Query.storage_parameters_for(client, client.table, true) || ""
|
146
156
|
primary_table_storage_parameters = Store.set(:primary_table_storage_parameters, result)
|
147
157
|
|
148
158
|
logger.debug("Disabling vacuum on shadow and audit table",
|
@@ -156,32 +166,39 @@ module PgOnlineSchemaChange
|
|
156
166
|
autovacuum_enabled = false, toast.autovacuum_enabled = false
|
157
167
|
);
|
158
168
|
SQL
|
159
|
-
Query.run(client.connection, sql)
|
169
|
+
Query.run(client.connection, sql, true)
|
160
170
|
end
|
161
171
|
|
162
172
|
def run_alter_statement!
|
173
|
+
# re-uses transaction with serializable
|
163
174
|
statement = Query.alter_statement_for(client, shadow_table)
|
164
175
|
logger.info("Running alter statement on shadow table",
|
165
176
|
{ shadow_table: shadow_table, parent_table: client.table })
|
166
|
-
Query.run(client.connection, statement)
|
177
|
+
Query.run(client.connection, statement, true)
|
167
178
|
|
168
179
|
Store.set(:dropped_columns_list, Query.dropped_columns(client))
|
169
180
|
Store.set(:renamed_columns_list, Query.renamed_columns(client))
|
170
181
|
end
|
171
182
|
|
172
|
-
# Begin the process to copy data into copy table
|
173
|
-
# depending on the size of the table, this can be a time
|
174
|
-
# taking operation.
|
175
183
|
def copy_data!
|
176
|
-
|
184
|
+
# re-uses transaction with serializable
|
185
|
+
# Begin the process to copy data into copy table
|
186
|
+
# depending on the size of the table, this can be a time
|
187
|
+
# taking operation.
|
188
|
+
logger.info("Clearing contents of audit table before copy..",
|
189
|
+
{ shadow_table: shadow_table, parent_table: client.table })
|
190
|
+
Query.run(client.connection, "DELETE FROM #{audit_table}", true)
|
177
191
|
|
192
|
+
logger.info("Copying contents..", { shadow_table: shadow_table, parent_table: client.table })
|
178
193
|
if client.copy_statement
|
179
194
|
query = format(client.copy_statement, shadow_table: shadow_table)
|
180
|
-
return Query.run(client.connection, query)
|
195
|
+
return Query.run(client.connection, query, true)
|
181
196
|
end
|
182
197
|
|
183
198
|
sql = Query.copy_data_statement(client, shadow_table)
|
184
|
-
Query.run(client.connection, sql)
|
199
|
+
Query.run(client.connection, sql, true)
|
200
|
+
ensure
|
201
|
+
Query.run(client.connection, "COMMIT;") # commit the serializable transaction
|
185
202
|
end
|
186
203
|
|
187
204
|
def replay_and_swap!
|
@@ -60,7 +60,7 @@ module PgOnlineSchemaChange
|
|
60
60
|
result
|
61
61
|
end
|
62
62
|
|
63
|
-
def table_columns(client, table = nil)
|
63
|
+
def table_columns(client, table = nil, reuse_trasaction = false)
|
64
64
|
sql = <<~SQL
|
65
65
|
SELECT attname as column_name, format_type(atttypid, atttypmod) as type, attnum as column_position FROM pg_attribute
|
66
66
|
WHERE attrelid = \'#{table || client.table}\'::regclass AND attnum > 0 AND NOT attisdropped
|
@@ -68,7 +68,7 @@ module PgOnlineSchemaChange
|
|
68
68
|
SQL
|
69
69
|
mapped_columns = []
|
70
70
|
|
71
|
-
run(client.connection, sql) do |result|
|
71
|
+
run(client.connection, sql, reuse_trasaction) do |result|
|
72
72
|
mapped_columns = result.map do |row|
|
73
73
|
row["column_name_regular"] = row["column_name"]
|
74
74
|
row["column_name"] = client.connection.quote_ident(row["column_name"])
|
@@ -210,13 +210,13 @@ module PgOnlineSchemaChange
|
|
210
210
|
columns.first
|
211
211
|
end
|
212
212
|
|
213
|
-
def storage_parameters_for(client, table)
|
213
|
+
def storage_parameters_for(client, table, reuse_trasaction = false)
|
214
214
|
query = <<~SQL
|
215
215
|
SELECT array_to_string(reloptions, ',') as params FROM pg_class WHERE relname=\'#{table}\';
|
216
216
|
SQL
|
217
217
|
|
218
218
|
columns = []
|
219
|
-
run(client.connection, query) do |result|
|
219
|
+
run(client.connection, query, reuse_trasaction) do |result|
|
220
220
|
columns = result.map { |row| row["params"] }
|
221
221
|
end
|
222
222
|
|
@@ -266,8 +266,8 @@ module PgOnlineSchemaChange
|
|
266
266
|
run(client.connection, query, true)
|
267
267
|
end
|
268
268
|
|
269
|
-
def copy_data_statement(client, shadow_table)
|
270
|
-
select_columns = table_columns(client).map do |entry|
|
269
|
+
def copy_data_statement(client, shadow_table, reuse_trasaction = false)
|
270
|
+
select_columns = table_columns(client, client.table, reuse_trasaction).map do |entry|
|
271
271
|
entry["column_name_regular"]
|
272
272
|
end
|
273
273
|
|
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.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shayon Mukherjee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-02-
|
11
|
+
date: 2022-02-17 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: pg-online-schema-change (pg-osc) is a tool for making schema changes
|
14
14
|
(any ALTER statements) in Postgres tables with minimal locks, thus helping achieve
|