pg_easy_replicate 0.1.5 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/README.md +2 -1
- data/lib/pg_easy_replicate/orchestrate.rb +69 -21
- data/lib/pg_easy_replicate/version.rb +1 -1
- data/scripts/e2e-bootstrap.sh +19 -0
- data/scripts/e2e-start.sh +18 -0
- metadata +5 -6
- data/bin/test.sh +0 -28
- /data/{bin → scripts}/release.sh +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8b855bf14bd72bcf4ace60b2f746139d76408d6a00e0ae7ec3fd2d0c276cf22b
|
4
|
+
data.tar.gz: 1eff23811d7e9d8608c241bcc7a67367c8de8271ea53d6960f57859aa5c186fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a7de21167ee2027115972442b9aa859fba7954bdc2e04b8e27e92d6fbadd2e7c9eb92c700d01f69ba9fecd5b40fe5593b5d0467ba4b2766106a7951311401842
|
7
|
+
data.tar.gz: c38c45df66c9a4433b558a41f120f8c3304b5cbd9ea1072348df61397b7e51b38d1709b83c160917d69fbcf460e18dc9f9355ea7c96ae3d109ba40b4430ff06c
|
data/.rspec
CHANGED
data/CHANGELOG.md
CHANGED
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# pg_easy_replicate
|
2
2
|
|
3
3
|
[![CI](https://github.com/shayonj/pg_easy_replicate/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/shayonj/pg_easy_replicate/actions/workflows/ci.yaml)
|
4
|
-
[![
|
4
|
+
[![Smoke spec](https://github.com/shayonj/pg_easy_replicate/actions/workflows/smoke.yaml/badge.svg?branch=main)](https://github.com/shayonj/pg_easy_replicate/actions/workflows/ci.yaml)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/pg_easy_replicate.svg?1)](https://badge.fury.io/rb/pg_easy_replicate)
|
5
6
|
|
6
7
|
`pg_easy_replicate` is a CLI orchestrator tool that simplifies the process of setting up [logical replication](https://www.postgresql.org/docs/current/logical-replication.html) between two PostgreSQL databases. `pg_easy_replicate` also supports switchover. After the source (primary database) is fully replicating, `pg_easy_replicate` puts it into read-only mode and via logical replication flushes all data to the new target database. This ensures zero data loss and minimal downtime for the application. This method can be useful for performing minimal downtime (up to <1min, depending) major version upgrades between two PostgreSQL databases, load testing with blue/green database setup and other similar use cases.
|
7
8
|
|
@@ -9,6 +9,14 @@ module PgEasyReplicate
|
|
9
9
|
DEFAULT_WAIT = 5 # seconds
|
10
10
|
|
11
11
|
def start_sync(options)
|
12
|
+
schema_name = options[:schema_name] || "public"
|
13
|
+
tables =
|
14
|
+
determine_tables(
|
15
|
+
schema: schema_name,
|
16
|
+
conn_string: source_db_url,
|
17
|
+
list: options[:tables],
|
18
|
+
)
|
19
|
+
|
12
20
|
create_publication(
|
13
21
|
group_name: options[:group_name],
|
14
22
|
conn_string: source_db_url,
|
@@ -16,9 +24,9 @@ module PgEasyReplicate
|
|
16
24
|
|
17
25
|
add_tables_to_publication(
|
18
26
|
group_name: options[:group_name],
|
19
|
-
tables:
|
27
|
+
tables: tables,
|
20
28
|
conn_string: source_db_url,
|
21
|
-
schema:
|
29
|
+
schema: schema_name,
|
22
30
|
)
|
23
31
|
|
24
32
|
create_subscription(
|
@@ -29,8 +37,8 @@ module PgEasyReplicate
|
|
29
37
|
|
30
38
|
Group.create(
|
31
39
|
name: options[:group_name],
|
32
|
-
table_names:
|
33
|
-
schema_name:
|
40
|
+
table_names: tables,
|
41
|
+
schema_name: schema_name,
|
34
42
|
started_at: Time.now.utc,
|
35
43
|
)
|
36
44
|
rescue => e
|
@@ -45,8 +53,8 @@ module PgEasyReplicate
|
|
45
53
|
else
|
46
54
|
Group.create(
|
47
55
|
name: options[:group_name],
|
48
|
-
table_names:
|
49
|
-
schema_name:
|
56
|
+
table_names: tables,
|
57
|
+
schema_name: schema_name,
|
50
58
|
started_at: Time.now.utc,
|
51
59
|
failed_at: Time.now.utc,
|
52
60
|
)
|
@@ -80,19 +88,17 @@ module PgEasyReplicate
|
|
80
88
|
"Adding tables up publication",
|
81
89
|
{ publication_name: publication_name(group_name) },
|
82
90
|
)
|
83
|
-
tables = tables&.split(",") || []
|
84
|
-
unless tables.size > 0
|
85
|
-
tables = list_all_tables(schema: schema, conn_string: conn_string)
|
86
|
-
end
|
87
91
|
|
88
|
-
tables
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
92
|
+
tables
|
93
|
+
.split(",")
|
94
|
+
.map do |table_name|
|
95
|
+
Query.run(
|
96
|
+
query:
|
97
|
+
"ALTER PUBLICATION #{publication_name(group_name)} ADD TABLE \"#{table_name}\"",
|
98
|
+
connection_url: conn_string,
|
99
|
+
schema: schema,
|
100
|
+
)
|
101
|
+
end
|
96
102
|
rescue => e
|
97
103
|
raise "Unable to add tables to publication: #{e.message}"
|
98
104
|
end
|
@@ -101,11 +107,12 @@ module PgEasyReplicate
|
|
101
107
|
Query
|
102
108
|
.run(
|
103
109
|
query:
|
104
|
-
"SELECT table_name FROM information_schema.tables WHERE table_schema = '#{schema}'",
|
110
|
+
"SELECT table_name FROM information_schema.tables WHERE table_schema = '#{schema}' ORDER BY table_name",
|
105
111
|
connection_url: conn_string,
|
106
112
|
)
|
107
113
|
.map(&:values)
|
108
114
|
.flatten
|
115
|
+
.join(",")
|
109
116
|
end
|
110
117
|
|
111
118
|
def drop_publication(group_name:, conn_string:)
|
@@ -197,11 +204,16 @@ module PgEasyReplicate
|
|
197
204
|
group_name:,
|
198
205
|
source_conn_string: source_db_url,
|
199
206
|
target_conn_string: target_db_url,
|
200
|
-
lag_delta_size:
|
207
|
+
lag_delta_size: nil
|
201
208
|
)
|
202
209
|
group = Group.find(group_name)
|
203
210
|
|
204
|
-
|
211
|
+
run_vacuum_analyze(
|
212
|
+
conn_string: target_conn_string,
|
213
|
+
tables: group[:table_names],
|
214
|
+
schema: group[:schema_name],
|
215
|
+
)
|
216
|
+
watch_lag(group_name: group_name, lag: lag_delta_size || DEFAULT_LAG)
|
205
217
|
revoke_connections_on_source_db(group_name)
|
206
218
|
wait_for_remaining_catchup(group_name)
|
207
219
|
refresh_sequences(
|
@@ -209,6 +221,12 @@ module PgEasyReplicate
|
|
209
221
|
schema: group[:schema_name],
|
210
222
|
)
|
211
223
|
mark_switchover_complete(group_name)
|
224
|
+
# Run vacuum analyze to refresh the planner post switchover
|
225
|
+
run_vacuum_analyze(
|
226
|
+
conn_string: target_conn_string,
|
227
|
+
tables: group[:table_names],
|
228
|
+
schema: group[:schema_name],
|
229
|
+
)
|
212
230
|
drop_subscription(
|
213
231
|
group_name: group_name,
|
214
232
|
target_conn_string: target_conn_string,
|
@@ -317,9 +335,39 @@ module PgEasyReplicate
|
|
317
335
|
raise "Unable to refresh sequences: #{e.message}"
|
318
336
|
end
|
319
337
|
|
338
|
+
def run_vacuum_analyze(conn_string:, tables:, schema:)
|
339
|
+
tables
|
340
|
+
.split(",")
|
341
|
+
.each do |t|
|
342
|
+
logger.info(
|
343
|
+
"Running vacuum analyze on #{t}",
|
344
|
+
schema: schema,
|
345
|
+
table: t,
|
346
|
+
)
|
347
|
+
Query.run(
|
348
|
+
query: "VACUUM VERBOSE ANALYZE #{t}",
|
349
|
+
connection_url: conn_string,
|
350
|
+
schema: schema,
|
351
|
+
transaction: false,
|
352
|
+
)
|
353
|
+
end
|
354
|
+
rescue => e
|
355
|
+
raise "Unable to run vacuum and analyze: #{e.message}"
|
356
|
+
end
|
357
|
+
|
320
358
|
def mark_switchover_complete(group_name)
|
321
359
|
Group.update(group_name: group_name, switchover_completed_at: Time.now)
|
322
360
|
end
|
361
|
+
|
362
|
+
private
|
363
|
+
|
364
|
+
def determine_tables(schema:, conn_string:, list: "")
|
365
|
+
tables = list&.split(",") || []
|
366
|
+
unless tables.size > 0
|
367
|
+
return list_all_tables(schema: schema, conn_string: conn_string)
|
368
|
+
end
|
369
|
+
""
|
370
|
+
end
|
323
371
|
end
|
324
372
|
end
|
325
373
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -eo pipefail
|
4
|
+
|
5
|
+
if [[ -z ${GITHUB_WORKFLOW} ]]; then
|
6
|
+
export SECONDARY_SOURCE_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@source_db/postgres"
|
7
|
+
fi
|
8
|
+
|
9
|
+
export SOURCE_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@localhost:5432/postgres"
|
10
|
+
export TARGET_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@localhost:5433/postgres"
|
11
|
+
export PGPASSWORD='jamesbond123@7!'"'"'3aaR'
|
12
|
+
|
13
|
+
pgbench --initialize -s 5 --foreign-keys --host localhost -U jamesbond -d postgres
|
14
|
+
|
15
|
+
pg_dump --schema-only --host localhost -U jamesbond -d postgres >schema.sql
|
16
|
+
cat schema.sql | psql --host localhost -U jamesbond -d postgres -p 5433
|
17
|
+
rm schema.sql
|
18
|
+
|
19
|
+
bundle exec bin/pg_easy_replicate config_check
|
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -eo pipefail
|
4
|
+
|
5
|
+
if [[ -z ${GITHUB_WORKFLOW} ]]; then
|
6
|
+
export SECONDARY_SOURCE_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@source_db/postgres"
|
7
|
+
fi
|
8
|
+
|
9
|
+
export SOURCE_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@localhost:5432/postgres"
|
10
|
+
export TARGET_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@localhost:5433/postgres"
|
11
|
+
export PGPASSWORD='jamesbond123@7!'"'"''"'"'3aaR'
|
12
|
+
|
13
|
+
# Bootstrap and cleanup
|
14
|
+
echo "===== Performing Bootstrap and cleanup"
|
15
|
+
bundle exec bin/pg_easy_replicate bootstrap -g cluster-1
|
16
|
+
bundle exec bin/pg_easy_replicate start_sync -g cluster-1 -s public
|
17
|
+
bundle exec bin/pg_easy_replicate stats -g cluster-1
|
18
|
+
bundle exec bin/pg_easy_replicate switchover -g cluster-1
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pg_easy_replicate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
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-06-
|
11
|
+
date: 2023-06-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ougai
|
@@ -241,8 +241,6 @@ email:
|
|
241
241
|
executables:
|
242
242
|
- pg_easy_replicate
|
243
243
|
- pg_easy_replicate_console
|
244
|
-
- release.sh
|
245
|
-
- test.sh
|
246
244
|
extensions: []
|
247
245
|
extra_rdoc_files: []
|
248
246
|
files:
|
@@ -260,8 +258,6 @@ files:
|
|
260
258
|
- Rakefile
|
261
259
|
- bin/pg_easy_replicate
|
262
260
|
- bin/pg_easy_replicate_console
|
263
|
-
- bin/release.sh
|
264
|
-
- bin/test.sh
|
265
261
|
- docker-compose.yml
|
266
262
|
- lib/pg_easy_replicate.rb
|
267
263
|
- lib/pg_easy_replicate/cli.rb
|
@@ -272,6 +268,9 @@ files:
|
|
272
268
|
- lib/pg_easy_replicate/stats.rb
|
273
269
|
- lib/pg_easy_replicate/version.rb
|
274
270
|
- package.json
|
271
|
+
- scripts/e2e-bootstrap.sh
|
272
|
+
- scripts/e2e-start.sh
|
273
|
+
- scripts/release.sh
|
275
274
|
- yarn.lock
|
276
275
|
homepage: https://github.com/shayonj/pg_easy_replicate
|
277
276
|
licenses:
|
data/bin/test.sh
DELETED
@@ -1,28 +0,0 @@
|
|
1
|
-
#!/bin/bash
|
2
|
-
|
3
|
-
set -euo pipefail
|
4
|
-
|
5
|
-
export SECONDARY_SOURCE_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@source_db/postgres"
|
6
|
-
export SOURCE_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@localhost:5432/postgres"
|
7
|
-
export TARGET_DB_URL="postgres://jamesbond:jamesbond123%407%21%273aaR@localhost:5433/postgres"
|
8
|
-
|
9
|
-
bundle exec bin/pg_easy_replicate config_check
|
10
|
-
|
11
|
-
# Bootstrap and cleanup
|
12
|
-
echo "===== Performing Bootstrap and cleanup"
|
13
|
-
bundle exec bin/pg_easy_replicate bootstrap -g cluster-1
|
14
|
-
bundle exec bin/pg_easy_replicate cleanup -e -g cluster-1
|
15
|
-
|
16
|
-
# Bootstrap and start_sync
|
17
|
-
echo "===== Performing Bootstrap, start_sync, stop_sync and cleanup"
|
18
|
-
bundle exec bin/pg_easy_replicate bootstrap -g cluster-1
|
19
|
-
bundle exec bin/pg_easy_replicate start_sync -g cluster-1
|
20
|
-
bundle exec bin/pg_easy_replicate stop_sync -g cluster-1
|
21
|
-
bundle exec bin/pg_easy_replicate cleanup -e -g cluster-1
|
22
|
-
|
23
|
-
# Bootstrap with switchover
|
24
|
-
echo "===== Performing Bootstrap, start_sync, stop_sync and cleanup"
|
25
|
-
bundle exec bin/pg_easy_replicate bootstrap -g cluster-1
|
26
|
-
bundle exec bin/pg_easy_replicate start_sync -g cluster-1
|
27
|
-
# bundle exec bin/pg_easy_replicate switchover -g cluster-1
|
28
|
-
bundle exec bin/pg_easy_replicate cleanup -e -g cluster-1
|
/data/{bin → scripts}/release.sh
RENAMED
File without changes
|