cpflow 6.0.0.rc.0 → 6.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.agents/agent-workflow.yml +16 -38
  3. data/.agents/bin/README.md +7 -8
  4. data/.agents/legacy-workflow-policy.yml +11 -0
  5. data/.agents/shaka.md +20 -0
  6. data/.agents/trusted-actions.yml +12 -0
  7. data/.coderabbit.yaml +1 -0
  8. data/.github/actions/cpflow-build-docker-image/action.yml +153 -22
  9. data/.github/actions/cpflow-delete-control-plane-app/delete-app.sh +3 -0
  10. data/.github/actions/cpflow-preview-promotion/action.yml +82 -0
  11. data/.github/actions/cpflow-preview-promotion/preview-promotion.sh +90 -0
  12. data/.github/workflows/cpflow-cleanup-stale-review-apps.yml +3 -10
  13. data/.github/workflows/cpflow-delete-review-app.yml +4 -11
  14. data/.github/workflows/cpflow-deploy-review-app.yml +19 -14
  15. data/.github/workflows/cpflow-deploy-staging.yml +17 -12
  16. data/.github/workflows/cpflow-promote-staging-to-production.yml +132 -13
  17. data/.github/workflows/rspec.yml +24 -0
  18. data/AGENTS.md +18 -3
  19. data/CHANGELOG.md +26 -3
  20. data/CONTRIBUTING.md +4 -4
  21. data/Gemfile.lock +1 -1
  22. data/README.md +5 -2
  23. data/docs/ai-github-flow-prompt.md +104 -8
  24. data/docs/ci-automation.md +258 -60
  25. data/docs/commands.md +32 -16
  26. data/docs/postgres-review-apps.md +90 -0
  27. data/docs/postgres.md +233 -375
  28. data/docs/secrets-and-env-values.md +42 -0
  29. data/docs/sidebars.ts +1 -0
  30. data/lib/command/ai_github_flow_prompt.rb +10 -13
  31. data/lib/command/ai_github_flow_prompt.txt +103 -0
  32. data/lib/command/apply_template.rb +36 -4
  33. data/lib/command/base.rb +56 -3
  34. data/lib/command/delete.rb +174 -4
  35. data/lib/command/deploy_image.rb +6 -0
  36. data/lib/command/generate.rb +247 -3
  37. data/lib/command/generate_github_actions.rb +15 -17
  38. data/lib/command/setup_app.rb +92 -10
  39. data/lib/command/update_github_actions.rb +107 -43
  40. data/lib/core/config.rb +73 -0
  41. data/lib/core/controlplane.rb +22 -2
  42. data/lib/core/controlplane_api.rb +28 -2
  43. data/lib/core/controlplane_api_direct.rb +17 -0
  44. data/lib/core/disposable_postgres_helpers.rb +167 -0
  45. data/lib/core/doctor_service.rb +7 -0
  46. data/lib/core/github_flow_readiness/checks.rb +2 -1
  47. data/lib/core/github_flow_readiness_service.rb +4 -0
  48. data/lib/core/repo_introspection.rb +112 -2
  49. data/lib/cpflow/version.rb +1 -1
  50. data/lib/generator_templates/.dockerignore +16 -0
  51. data/lib/generator_templates/Dockerfile +9 -5
  52. data/lib/generator_templates/entrypoint.sh +2 -0
  53. data/lib/generator_templates/templates/postgres.yml +9 -0
  54. data/lib/generator_templates_sqlite/controlplane.yml +2 -1
  55. data/lib/generator_templates_sqlite/release_script.sh +1 -1
  56. data/lib/generator_templates_sqlite/templates/rails.yml +2 -1
  57. data/lib/github_flow_templates/.github/cpflow-help.md +39 -12
  58. data/lib/github_flow_templates/.github/workflows/cpflow-deploy-review-app.yml +1 -0
  59. data/lib/github_flow_templates/.github/workflows/cpflow-deploy-staging.yml +1 -0
  60. data/lib/github_flow_templates/.github/workflows/cpflow-promote-staging-to-production.yml +132 -13
  61. data/lib/github_flow_templates/bin/pin-cpflow-github-ref +37 -13
  62. data/lib/github_flow_templates/bin/test-cpflow-github-flow +88 -47
  63. metadata +12 -3
data/docs/postgres.md CHANGED
@@ -1,447 +1,305 @@
1
- # Migrating Postgres database from Heroku infrastructure
1
+ # Migrating PostgreSQL from Heroku to RDS
2
+
3
+ This guide covers a database move from Heroku Postgres to AWS RDS using dump and
4
+ restore or Bucardo replication. Rehearse the chosen method with your database before
5
+ scheduling a production cutover.
6
+
7
+ For other PostgreSQL tasks, use these guides:
8
+
9
+ - [PostgreSQL for disposable review apps](./postgres-review-apps.md): create and
10
+ clean up review-app helpers.
11
+ - [Private RDS/Aurora networking](./rds-private-networking.md): connect Control Plane
12
+ workloads through the Cloud Wormhole agent.
13
+ - [Control Plane PostgreSQL Template Catalog](https://shakadocs.controlplane.com/template-catalog/templates/postgres):
14
+ run PostgreSQL on Control Plane with persistent storage and optional backups.
15
+
16
+ ## Choose a migration method
17
+
18
+ | Method | Application downtime | Main tradeoff |
19
+ | --- | --- | --- |
20
+ | Dump and restore | Writers stay stopped through the final dump and restore | Fewer moving parts; downtime grows with database size |
21
+ | Bucardo replication | Writers stop for the final catch-up and connection switch | More setup; the initial copy runs while the app stays online |
22
+
23
+ Bucardo uses triggers to copy data and replicate later changes. It needs permission
24
+ to install its objects on the source database. Confirm that your Heroku plan and
25
+ target database support the required permissions before choosing it.
26
+
27
+ Do not treat example timings as a downtime guarantee. Measure backup, restore, and
28
+ replication performance during a rehearsal. For background on the trigger-based
29
+ approach, see [AWS's Bucardo migration example](https://aws.amazon.com/blogs/database/migrating-legacy-postgresql-databases-to-amazon-rds-or-aurora-postgresql-using-bucardo/).
30
+
31
+ ### Historical large-database timings
32
+
33
+ The original guide recorded these approximate timings for a 1 TB database. It did
34
+ not record enough environment details to reproduce the results. Use them to
35
+ understand the difference between elapsed migration time and downtime, not to
36
+ estimate your migration:
37
+
38
+ | Method | Initial transfer | Application downtime |
39
+ | --- | --- | --- |
40
+ | Dump and restore | 2.5 hours to create a backup, 0.5 hours to download it, and 13 hours to restore with four workers | About 16 hours |
41
+ | Bucardo | About 1.5 days for the initial copy with one worker, while the source application remained online | About 1–2 minutes for the database switch |
42
+
43
+ Copy throughput and the source write rate determine how quickly replication can
44
+ catch up. More instance capacity does not automatically make a single-worker copy
45
+ parallel. Measure the initial copy and final catch-up separately during rehearsal,
46
+ including application shutdown, restart, and readiness checks in the downtime budget.
47
+
48
+ ## Prepare the databases and network
49
+
50
+ Before either migration method:
51
+
52
+ 1. Inventory database versions, extensions, storage, and every application or job
53
+ that writes to the database.
54
+ 2. Create the RDS database with compatible schema support and enough storage for
55
+ the data and expected growth.
56
+ 3. Establish connectivity from the migration host and the application's eventual
57
+ deployment location. Prefer private networking; see the
58
+ [RDS/Aurora networking guide](./rds-private-networking.md).
59
+ 4. Restrict security groups to the hosts and ports that need access. If temporary
60
+ public access is necessary, restrict it to the migration host and remove it afterward.
61
+ 5. Take a source backup and agree on validation, cutover, and rollback criteria.
62
+
63
+ RDS allocated storage cannot be reduced in place. Size it for the initial load,
64
+ indexes, and growth before starting; storage autoscaling may not keep up with a
65
+ large bulk load. Further storage changes must wait for storage optimization to
66
+ finish and are subject to modification limits. See
67
+ [RDS storage autoscaling limitations](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PIOPS.Autoscaling.html#autoscaling-limitations).
68
+
69
+ Choose the target database region based on application latency as well as migration
70
+ throughput. Test the network path while the application still runs on Heroku and
71
+ after its deployment moves. If the application already supports separate read and
72
+ write connections, a temporary read replica near the application may reduce read
73
+ latency. Writes still reach the primary, and reads must tolerate replication lag.
74
+ Rehearse this extra topology before relying on it; see
75
+ [RDS PostgreSQL read replicas](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_PostgreSQL.Replication.ReadReplicas.html).
76
+
77
+ For Bucardo, also check that each replicated table has a primary key. Freeze schema
78
+ changes from the schema copy through cutover. Plan separately for objects Bucardo
79
+ does not replicate, including materialized views.
80
+
81
+ ### Set up a migration host
82
+
83
+ Use a temporary host, such as an EC2 instance, that can reach both databases. Choose
84
+ its region, capacity, and disk space based on measured latency and copy requirements.
85
+ Restrict SSH access and protect the private key:
2
86
 
3
- > **Networking note.** This guide is written against a *publicly reachable* RDS instance for simplicity of the
4
- > Bucardo migration steps. For production, you almost certainly want RDS/Aurora in private subnets reached from
5
- > Control Plane workloads via the Cloud Wormhole agent — see
6
- > [Connecting Control Plane workloads to a private AWS RDS/Aurora database](./rds-private-networking.md). The
7
- > Bucardo migration here still works against a private RDS once the network path is set up.
8
-
9
- If you are replacing Heroku Postgres or another pre-provisioned database service, also review the
10
- [Control Plane PostgreSQL Template Catalog page](https://shakadocs.controlplane.com/template-catalog/templates/postgres). The
11
- catalog template covers a single-instance PostgreSQL workload with persistent storage, optional PgBouncer, and optional
12
- scheduled backups.
13
-
14
- One of the biggest problems that will appear when moving from Heroku infrastructure is migrating the database. And
15
- despite it being rather easy if done between Heroku-hosted databases or non-Heroku-hosted databases (as Postgres has
16
- tools to do that naturally) it is not easily possible between Heroku and anything outside Heroku,
17
- **as Heroku doesn't allow setting up WAL replication for Postgres**. Period. No... any... replication outside of
18
- Heroku infrastructure for Postgres.
19
-
20
- Previously, it was said to be possible to ask Heroku support to manually set up WAL log shipping, but they
21
- don't want to do that anymore. Which leaves only 2 options:
22
-
23
- ### Option A: dump and restore way
24
-
25
- Nothing problematic here in general **if you can withstand long application maintenance time**.
26
- You basically need to:
27
-
28
- 1. enable maintenance
29
- 2. stop the application completely and wait for all the database writes to finish
30
- 3. dump database on Heroku
31
- 4. restore the database on RDS
32
- 5. start the application
33
- 6. disable maintenance
34
-
35
- And if the database is small or it is a hobby app, this should not be looked any further.
36
- However, this is not acceptable for 99% of production apps as their databases are huge and maintenance time
37
- should be as small as possible.
38
-
39
- Rough timing for a 1Tb database can be (but your mileage may vary):
40
-
41
- - 2.5h creating Heroku backup
42
- - 0.5h downloading backup to EC2
43
- - 13h restoring a backup on RDS (in 4 threads)
44
-
45
- **~16h total time, equals maintenance downtime**
46
-
47
- ### Option B: logical replication way
48
-
49
- There are several logical replication solutions exist for Postgres - Slony, Bucardo, Londiste, Mimeo, but... when
50
- you try to dig deeper, the only viable and up-to-date solution for purpose of migrating from Heroku to RDS is Bucardo.
51
-
52
- The migration process with Bucardo looks as follows:
53
-
54
- 1. setup Bucardo on the dedicated EC2 instance
55
- 2. dump Heroku database schema and restore it on RDS - rather fast as there is no data
56
- 3. start Bucardo replication - this will install triggers and special schema to your database
57
- 4. wait for replication to catch up - this may take a long time, but the application can continue working as usual
58
- 5. enable maintenance
59
- 6. stop the application completely and wait for replication to finally finish
60
- 7. switch the database connection strings
61
- 8. start the application
62
- 9. disable maintenance
63
-
64
- Maintenance downtime here can be minutes not hours or days like in p1, but no free lunches - the process is more complex.
65
-
66
- Rough timing for a 1Tb database can be (but your mileage may vary):
67
-
68
- - whatever setup time, no hurry
69
- - 1.5 days for onetimecopy (in 1 thread) - DDL changes not allowed, but no downtime
70
- - 1-2 min for database switch, maintenance downtime
71
-
72
- **~2 days total time, ~1-2 min maintenance downtime**
73
-
74
- ### Some considerations:
75
-
76
- - DDL changes should be "frozen and postponed" while Bucardo replication. There is also a way to stop replication,
77
- update DDL in both databases, and restart replication, however, as well no-DDL for a day or two seems a
78
- reasonable restriction for production databases vs potential errors.
79
-
80
- - there is a "speed up" option to restore dump (with threads) and then run Bucardo to catch up only deltas, but it
81
- looks unnecessary as speed gain is minimal vs potential errors. It will not speed up things dramatically, but will just
82
- save a couple of hours of non-maintenance time (which most probably be spent on the command line) so not worth doing.
83
-
84
- ## Before replication
85
-
86
- ### Application code changes
87
-
88
- Before everything, we need to recheck the database schema and ensure **that every table has a primary key (PK)
89
- in place** as Bucardo is using PKs for replication.
90
-
91
- > NOTE: theoretically Bucardo can work with uniq indexes as well, but having a PK on each table is easy and avoids
92
- unnesessary complications
93
-
94
- So, please stop, and do whatever is needed for your application.
95
-
96
- ### Choosing database location and EC2 location
97
-
98
- All Heroku Postgres databases for location US are running in AWS on `us-east-1`. Control Plane, on the other side,
99
- recommends `us-east-2` as a default. So we need to choose:
100
-
101
- - either simple setup - main database in `us-east-2`
102
- - or a bit more complex - main database in `us-east-2`, replica in `us-east-1` (which can be removed later)
103
-
104
- This makes sense if your application supports working with replicas. Then read-only `SELECT` queries will go to the
105
- read replica and write `INSERT/UPDATE` queries will go to the main write-enabled database.
106
- This way we can keep most reading latency to the minimum.
107
-
108
- Anyway, it is worth to consider developing such a mode in the application if you want to scale in more than 1 region.
109
-
110
- ### Create new EC2 instance which we will use for database replication
111
-
112
- - better if it will be in the same AWS location where RDS database will be (most probably `us-east-2`)
113
- - choose Ubuntu as OS
114
- - use some bigger instance, e.g. `m6i.4xlarge` - price doesn't matter much, as such instance will not run long time
115
- - if you will be copying backup via this instance, choose sufficient space for both OS and backup and some free space
116
- - create security group `public-access` with all inbound and outbound traffic allowed. this will be handy as well for
117
- database setup. if you need tighter access controls, up to you
118
- - generate a new certificate and save it locally (e.g. `bucardo.pem`), will be used for SSH connection. Do not forget to
119
- update correct permissions e.g. `chown TODO ~/Dowloads/bucardo.pem`
120
-
121
- After the instance will be running on AWS, you can connect to it via SSH as follows:
122
87
  ```sh
123
- ssh ubuntu@1.2.3.4 -i ~/Downloads/bucardo.pem
88
+ chmod 600 ~/Downloads/bucardo.pem
89
+ ssh -i ~/Downloads/bucardo.pem ubuntu@MIGRATION_HOST
124
90
  ```
125
91
 
126
- ### Creating RDS instance
127
-
128
- - check `public` box
129
- - pick `public-access` security group (or whatever you need)
130
- - if you will be restoring from backup, it is possible to choose temporary bigger instance e.g. `db.r6i.4xlarge`, which
131
- can be downgraded later.
132
- - if you will be using bucardo onetimecopy, then it is ok to select any instance you may need, as bucardo does copying
133
- in a single thread
134
- - it is fairly easy to switch database instance type afterwards and requires only minimal downtime
135
- - storage space. this needs a good pick, as it is a) not possible to shrink and b) auto-expanding which AWS offers and
136
- which should be enabled by default can block database modifications for quite long periods (days)
92
+ Run long foreground operations in a detachable session such as `screen`, and save
93
+ logs so you can inspect errors after reconnecting.
137
94
 
138
- ### Running commands in detached mode on the EC2 instance
95
+ ### Install database tools
139
96
 
140
- Some commands that run on EC2 may take a long time and we may want to disconnect from the SSH session while the command
141
- will continue running. And we want to reconnect to the session and see the progress. Possibly without installing
142
- special tools. This can be accomplished with `screen` command, e.g.:
97
+ Install PostgreSQL clients compatible with the source and target versions. Follow
98
+ the [PostgreSQL installation instructions](https://www.postgresql.org/download/)
99
+ and [supported-version policy](https://www.postgresql.org/support/versioning/).
143
100
 
144
- ```sh
145
- # this will start a backround process and return to terminal (which can be closed)
146
- screen -dmL ...your command...
147
-
148
- # checking if screen is still running in the background
149
- ps aux | grep -i screen
101
+ For replication, install Bucardo and its dependencies using the
102
+ [Bucardo installation guide](https://bucardo.org/Bucardo/Installation/) and
103
+ [requirements](https://bucardo.org/Bucardo/installation/requirements). Bucardo needs
104
+ a local PostgreSQL database for its control metadata. Before initializing it, check:
150
105
 
151
- # see the output log
152
- cat screenlog.0
153
- ```
106
+ - Perl and the `DBI`, `DBD::Pg`, and `DBIx::Safe` modules, plus the utility modules
107
+ listed in Bucardo's requirements, are installed for the daemon's Perl runtime.
108
+ - The local control database has `PL/pgSQL` and `PL/PerlU` available. Replicated
109
+ databases need `PL/pgSQL` for the trigger-based sync; they do not need `PL/PerlU`.
110
+ - The installer can authenticate as a PostgreSQL superuser on the local control
111
+ server. This is separate from the source and target database privileges.
112
+ - The configured PID and log directories exist and are writable by the OS user
113
+ running Bucardo. Check the paths chosen by your installation rather than assuming
114
+ a particular distribution's layout.
154
115
 
155
- ### Installing Postgres and Bucardo on EC2
156
-
157
- Now, when RDS is running and EC2 is running we can start installing local Postgres and Bucardo itself. Let's install
158
- Postgres 13 first. It may be possible to install the latest Postgres, but 13 seems the best choice atm.
159
-
160
- ```sh
161
- # update all your packages
162
- sudo apt update
163
- sudo apt upgrade -y
164
-
165
- # add postgres repository key
166
- sudo sh -c 'echo "deb [arch=$(dpkg --print-architecture)] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
167
-
168
- wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
169
-
170
- # update again
171
- sudo apt-get update
172
-
173
- # install packages
174
- sudo apt-get -y install make postgresql-13 postgresql-client-13 postgresql-plperl-13 libdbd-pg-perl libdbix-safe-perl
175
- ```
176
-
177
- Postgres Perl language `plperl` as well as DBD and DBIx packages are needed for Bucardo.
178
-
179
- Now, as all dependencies are installed, we can install Bucardo from latest tarball.
116
+ Configure authenticated local access to that control database, then initialize it:
180
117
 
181
118
  ```sh
182
- # install Bucardo itself
183
- wget https://bucardo.org/downloads/Bucardo-5.6.0.tar.gz
184
- tar xzf Bucardo-5.6.0.tar.gz
185
- cd Bucardo-5.6.0
186
- perl Makefile.PL
187
- sudo make install
188
-
189
- # create dirs and fix permissions
190
- sudo mkdir /var/run/bucardo
191
- sudo mkdir /var/log/bucardo
192
- sudo chown ubuntu /var/run/bucardo/
193
- sudo chown ubuntu /var/log/bucardo
194
- ```
195
-
196
- After that, Bucardo is physically installed as a package and runnable but we need to configure everything.
197
- Let's start with Postgres. As this is a temporary installation (only for the period of replication),
198
- it is rather safe to set `trust` localhost connections (or set up another way if you want this).
199
-
200
-
201
- For this, we need to edit `pg_hba.conf` as follows:
202
- ```sh
203
- # edit pg config to make postgres trusted
204
- sudo nano /etc/postgresql/13/main/pg_hba.conf
205
- ```
206
-
207
- in that file change the following lines to `trust`
208
- ```sh
209
- # in pg_hba.conf
210
- local all postgres trust
211
- local all all trust
212
- ```
213
-
214
- and restart Postgres to pick up changes
215
- ```sh
216
- # restart postgres
217
- sudo systemctl restart postgresql
218
- ```
219
-
220
- And finally-finally we can install Bucardo service database on local Postgres and see if everything runs.
221
-
222
- ```sh
223
- # this will create local bucardo "service" database
224
119
  bucardo install
225
-
226
- # for option 3 pick `postgres` as a user
227
- # for option 4 pick `postgres` as a database from where initial connection should be attempted
228
120
  ```
229
121
 
230
- :tada: :tada: :tada: now we have local Postgres and Bucardo running, and can continue with external services
231
- configuration.
232
-
233
- ### Configuring external (Heroku, RDS) database connections
122
+ Test your chosen versions and authentication settings during rehearsal. The steps
123
+ below describe the replication flow; they do not replace Bucardo's installation guide.
234
124
 
235
- For this, we will use `pg_service.conf`. It will not work in all places, and sometimes we will need to provide
236
- connection properties manually, but for many commands, it is very useful.
125
+ ### Configure source and target connections
237
126
 
238
- ```sh
239
- # create and edit .pg_service.conf
240
- touch ~/.pg_service.conf
241
- nano ~/.pg_service.conf
242
- ```
127
+ Create `~/.pg_service.conf` on the migration host. Replace the placeholders with
128
+ credentials for the source and target databases:
243
129
 
244
130
  ```ini
245
- # ~/.pg_service.conf
246
-
247
131
  [heroku]
248
- host=ec2-xxx.compute-1.amazonaws.com
132
+ host=HEROKU_HOST
249
133
  port=5432
250
- dbname=xxx
251
- user=xxx
252
- password=xxx
134
+ dbname=SOURCE_DATABASE
135
+ user=SOURCE_USER
136
+ password=SOURCE_PASSWORD
137
+ sslmode=require
253
138
 
254
139
  [rds]
255
- host=xxx.us-east-2.rds.amazonaws.com
140
+ host=RDS_HOST
256
141
  port=5432
257
- dbname=xxx
258
- user=postgres
259
- password=xxx
142
+ dbname=TARGET_DATABASE
143
+ user=TARGET_USER
144
+ password=TARGET_PASSWORD
145
+ sslmode=require
260
146
  ```
261
147
 
262
- Test connectivity to databases with:
263
- ```sh
264
- psql service=heroku -c '\l+'
265
- psql service=rds -c '\l+'
266
- ```
148
+ Protect this file and confirm that both connections work. Use your provider's
149
+ certificate settings if your environment requires server identity verification.
267
150
 
268
- You will see all databases set up on each server (and can see their size):
269
- ```console
270
- Name | Owner | Encoding | Collate | Ctype | Access privileges | Size | Tablespace | Description
271
- -------------------+----------+----------+-------------+-------------+-----------------------+-----------+------------+--------------------------------------------
272
- my-production-db | xxx | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | 821 GB | pg_default |
273
- postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | 8205 kB | pg_default | default administrative connection database
274
- rdsadmin | rdsadmin | UTF8 | en_US.UTF-8 | en_US.UTF-8 | rdsadmin=CTc/rdsadmin+| No Access | pg_default |
275
- | | | | | rdstopmgr=Tc/rdsadmin | | |
276
- template0 | rdsadmin | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/rdsadmin +| 8033 kB | pg_default | unmodifiable empty database
277
- | | | | | rdsadmin=CTc/rdsadmin | | |
278
- template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | =c/postgres +| 8205 kB | pg_default | default template for new databases
279
- | | | | | postgres=CTc/postgres | | |
280
- (5 rows)
151
+ ```sh
152
+ chmod 600 ~/.pg_service.conf
153
+ psql service=heroku -c 'SELECT current_database();'
154
+ psql service=rds -c 'SELECT current_database();'
281
155
  ```
282
156
 
283
- After both databases are connectable, we can proceed with replication itself.
157
+ ## Option A: dump and restore
284
158
 
285
- ## Performing replication
159
+ For a migration that can tolerate the measured restore time:
286
160
 
287
- :fire: :fire: :fire: **IMPORTANT: from this step, DDL changes are not allowed** :fire: :fire: :fire:
161
+ 1. Enable maintenance mode and stop **all database writers**, including background
162
+ jobs and external services. Maintenance mode alone does not stop writes.
163
+ 2. Wait for active writes to finish, then create and download the final source dump.
164
+ 3. Restore the dump into RDS and check every restore error.
165
+ 4. Validate the target data and application connection.
166
+ 5. Follow the [cutover steps](#switch-the-application-to-rds) below.
288
167
 
289
- ### Application changes
168
+ Keep writers stopped throughout the final dump and restore. The
169
+ [Heroku PGBackups guide](https://devcenter.heroku.com/articles/heroku-postgres-backups)
170
+ covers backup capture and download; choose and rehearse a restore procedure suitable
171
+ for your dump format and database size.
290
172
 
291
- - temporary freeze all DDL changes till replication will finish
292
- - temporary disable all background jobs or services that are possible to stop. This will help to lessen database
293
- load and especially, where possible, to decrease database write operations that will decrease replication pipe as well.
173
+ ## Option B: Bucardo replication
294
174
 
295
- ### Dump and restore the initial schema
175
+ ### Copy the schema
296
176
 
297
- This step doesn't take much time (as it is only a database schema without data),
298
- but it is definitely handy to save all output, and closely check for any errors.
177
+ Freeze DDL changes and reduce avoidable write traffic. Copy the source schema into
178
+ the empty target database, retaining logs and checking for errors:
299
179
 
300
180
  ```sh
301
- # save heroku schema to `schema.sql`
302
181
  pg_dump service=heroku --schema-only --no-acl --no-owner -v > schema.sql
303
-
304
- # restore `schema.sql` on RDS
305
- psql service=rds < schema.sql
182
+ psql --single-transaction -v ON_ERROR_STOP=1 -f schema.sql service=rds
306
183
  ```
307
184
 
308
- ### Configure Bucardo replication
185
+ The restore runs in one transaction and rolls back on error. Investigate any
186
+ unsupported extensions or other schema incompatibilities before retrying.
309
187
 
310
- After we have all databases connectable and all same schema, we can tell Bucardo what it needs to replicate:
311
- ```sh
312
- # add databases
313
- bucardo add db from_db dbhost=xxx dbport=5432 dbuser=xxx dbpass=xxx dbname=xxx
314
- bucardo add db to_db dbhost=xxx dbport=5432 dbuser=postgres dbpass=xxx dbname=xxx
188
+ ### Configure the sync
315
189
 
316
- # mark all tables and sequences for replication
317
- bucardo add all tables
318
- bucardo add all sequences
319
- ```
320
- Here, Bucardo will connect to databases and collect object metadata for replication.
321
- After that, we can add sync as well:
190
+ Register the source and target with Bucardo. Replace the named placeholders with
191
+ the connection values for each database. These commands contain credentials; protect your terminal
192
+ history and any logs that capture them.
322
193
 
323
194
  ```sh
324
- # add sync itself
325
- bucardo add sync mysync tables=all dbs=from_db,to_db onetimecopy=1
326
- ```
195
+ bucardo add db from_db dbhost=HEROKU_HOST dbport=5432 dbuser=SOURCE_USER dbpass=SOURCE_PASSWORD dbname=SOURCE_DATABASE
196
+ bucardo add db to_db dbhost=RDS_HOST dbport=5432 dbuser=TARGET_USER dbpass=TARGET_PASSWORD dbname=TARGET_DATABASE
327
197
 
328
- The most important option here is `onetimecopy=1` which will tell Bucardo to perform initial data copying
329
- (when sync will start). Such a copying is done *in a single thread* by creating a pipe (via Bucardo) as follows:
330
- ```SQL
331
- -- on heroku
332
- COPY xxx TO STDOUT
333
- -- on rds
334
- COPY xxx FROM STDIN
198
+ bucardo add all tables db=from_db --relgroup=migration
199
+ bucardo add all sequences db=from_db --relgroup=migration
200
+ bucardo add sync mysync relgroup=migration dbs=from_db:source,to_db:target onetimecopy=1
335
201
  ```
336
202
 
337
- ### Run sync
203
+ Inspect the registered tables, sequences, and source/target roles before starting.
204
+ See the [Bucardo tutorial](https://bucardo.org/Bucardo/pgbench_example) for its
205
+ registration and inspection commands.
206
+ The `onetimecopy=1` setting requests an initial data copy, followed by replication
207
+ of subsequent changes.
338
208
 
339
- And now, when everything is ready, we can push the button and go for a long :coffee: or maybe even a weekend.
209
+ ### Start and monitor replication
340
210
 
341
211
  ```sh
342
- # starts Bucardo sync daemon
343
212
  bucardo start
344
- ```
345
-
346
- As well it is ok to disconnect from SSH as Bucardo daemon will continue working in background.
347
-
348
- ### Monitor status
349
-
350
- To check the progress of sync (from Bucardo perspective):
351
- ```sh
352
- # overall progress of all syncs
353
213
  bucardo status
354
-
355
- # single sync progress
356
214
  bucardo status mysync
357
215
  ```
358
216
 
359
- To check what's going on in databases directly:
217
+ Bucardo runs as a daemon. You can disconnect from SSH and reconnect to inspect its
218
+ status and logs. To inspect its database activity:
219
+
360
220
  ```sh
361
- # Bucardo adds a comment to it's queries, so it is fairly easy to grep those
362
- psql service=heroku -c 'select * from pg_stat_activity' | grep -i bucardo
363
- psql service=rds -c 'select * from pg_stat_activity' | grep -i bucardo
221
+ psql service=heroku -c 'SELECT * FROM pg_stat_activity' | grep -i bucardo
222
+ psql service=rds -c 'SELECT * FROM pg_stat_activity' | grep -i bucardo
364
223
  ```
365
224
 
366
- ### After replication will catch up, but before databases switch
367
-
368
- 1. Please do a sanity check of data in tables. E.g. check:
369
-
370
- - table `COUNT`
371
- - min/max of PK ids where applicable
372
- - min/max of `created_at/updated_at` where applicable
225
+ Wait for the initial copy to finish and investigate replication errors. Keep schema
226
+ changes frozen until cutover completes.
373
227
 
374
- 2. For p1 it is possible to use our checker script that will do this automatically (TODO)
228
+ ### Validate before cutover
375
229
 
376
- 3. Refresh materialized views manually (as they are not synced by Bucardo).
377
- Just go to `psql` and `REFRESH MATERIALIZED VIEW ...`
230
+ Compare source and target data with checks suited to the application:
378
231
 
379
- ## Switch databases
232
+ - Row counts and representative records.
233
+ - Minimum and maximum primary keys or timestamps, where meaningful.
234
+ - Sequence values and application-specific invariants.
235
+ - Objects outside replication, including refreshed materialized views.
380
236
 
381
- :fire: :fire: :fire: **This is final non reverisble step now** :fire: :fire: :fire:
382
- Before this point, all changes can be easily removed or reversed and database can stay on Heroku as it was before,
383
- afther this switch it is not possible (at least easily).
237
+ Checks taken while writes continue can differ. Repeat the final validation after
238
+ stopping writers and waiting for replication to catch up.
384
239
 
385
- So... after sync will catch up, basically it is needed to:
240
+ ## Switch the application to RDS
386
241
 
387
- 1. start maintenance mode on heroku `heroku maintenance:on`
388
- 2. scale down and stop all the dynos
389
- 3. wait a bit for all queries to finish and replication catch up latest changes
390
- 4. detach heroku postgres from DATABASE_URL
391
- 5. set `DATABASE_URL` to RDS url (plaintext now)
392
- 6. start dynos
393
- 7. wait for their readiness with `heroku ps:wait`
394
- 8. stop maintenance with `heroku maintenance:off`
395
- 9. :fire: **Now we are fully on RDS, so DDL changes are allowed** :fire:
396
- 10. you could gradually enable all background jobs and services which were temporary stopped
242
+ Rehearse these steps with the application's deployment process. If you are also
243
+ moving the app to Control Plane, prepare its database secret and workloads before
244
+ the cutover window.
397
245
 
398
- ## After switch
399
-
400
- As we now running on RDS, there is only single task left to do on Heroku - make a final backup of database and save it.
246
+ For classic Heroku Postgres, retain a second source attachment before detaching
247
+ `DATABASE`. Replace `SOURCE_ADDON` with the existing add-on identifier:
401
248
 
402
249
  ```sh
403
- # to capture backup (will take lots of time), can be disconnected
404
- heroku pg:backups:capture -a example-app
405
-
406
- # to get url of backup
407
- heroku pg:backups:url bXXXX -a example-app
250
+ heroku addons:attach SOURCE_ADDON --as MIGRATION_SOURCE -a APP
408
251
  ```
409
252
 
410
- Now you can download it locally or copy to S3 via EC2 as it will take quite some time and traffic.
253
+ Confirm that `MIGRATION_SOURCE_URL` refers to the source database. Leave this
254
+ attachment in place for backup and recovery; the cutover detaches only `DATABASE`.
255
+ For Advanced databases, establish the equivalent retained attachment using that
256
+ plan's commands during rehearsal.
257
+
258
+ 1. Enable maintenance mode. For a Heroku-hosted app, use `heroku maintenance:on -a APP`.
259
+ 2. Stop all writers: web dynos, workers, scheduled jobs, and external services.
260
+ 3. Wait for active writes to finish. With Bucardo, wait for the final replication
261
+ catch-up and verify the target again.
262
+ 4. Save the old connection configuration and add-on attachment details for recovery.
263
+ For a Heroku-hosted app, detach the attachment that owns `DATABASE_URL` before
264
+ setting the RDS connection. For classic Heroku Postgres, this is typically
265
+ `heroku addons:detach DATABASE -a APP`; confirm the attachment name first.
266
+ Advanced databases use a different attachment command. See the
267
+ [Heroku CLI attachment reference](https://devcenter.heroku.com/articles/heroku-cli-commands#heroku-addons-detach-attachment_name)
268
+ and [database-type-specific credential instructions](https://devcenter.heroku.com/articles/heroku-postgresql-credentials#detach-a-credential).
269
+ Keep the source database itself; do not destroy the add-on.
270
+ 5. With Bucardo, stop replication with `bucardo stop` after the final catch-up,
271
+ before any application writes reach RDS. Confirm that the sync has stopped and
272
+ keep source writers disabled.
273
+ 6. Update the app's database connection to RDS using its deployment secret/configuration
274
+ mechanism.
275
+ 7. Start the application and check readiness, database connectivity, and key operations.
276
+ For Heroku dynos, `heroku ps:wait -a APP` can check process readiness.
277
+ 8. End maintenance only after those checks pass. On Heroku, use `heroku maintenance:off -a APP`.
278
+ 9. Resume background jobs and scheduled writers. Lift the schema freeze once
279
+ replication is no longer needed.
280
+
281
+ **Rollback boundary:** before RDS accepts new application writes, the source can
282
+ remain the authoritative database. After new writes reach RDS, switching back to
283
+ Heroku requires a data reconciliation plan; changing the connection string alone
284
+ would lose those writes.
285
+
286
+ ## Finish the migration
287
+
288
+ 1. Retain a final Heroku backup according to your recovery policy. Keep the source
289
+ available until the agreed recovery window closes.
290
+ 2. Verify RDS backups, monitoring, storage, and application performance.
291
+ 3. Remove Bucardo's temporary replication objects when they are no longer needed,
292
+ following its cleanup procedure. Do not restart the old sync against the live target.
293
+ 4. Remove temporary network access, migration credentials, and the migration host.
294
+
295
+ To capture and locate a final Heroku backup, use a remaining source attachment or
296
+ add-on identifier for `SOURCE_ADDON`:
411
297
 
412
298
  ```sh
413
- # download dump to EC2
414
- screen -dmL time curl 'your-url' -o latest.dump
415
-
416
- # install aws cli (in a way reccomended by Amazon)
417
- # ...TODO...
418
-
419
- # configure aws credentials
420
- aws configure
421
-
422
- # check S3 access
423
- aws s3 ls
424
-
425
- # upload to S3
426
- screen -dmL time aws s3 cp latest.dump s3://my-dumps-bucket/ --region us-east-1
299
+ heroku pg:backups:capture SOURCE_ADDON -a APP
300
+ heroku pg:backups:url BACKUP_ID -a APP
427
301
  ```
428
302
 
429
- # Refs
430
-
431
- https://bucardo.org
432
-
433
- https://stackoverflow.com/questions/22264753/linux-how-to-install-dbdpg-module
434
-
435
- https://gist.github.com/luizomf/1a7994cf4263e10dce416a75b9180f01
436
-
437
- https://www.waytoeasylearn.com/learn/bucardo-installation/
438
-
439
- https://gist.github.com/knasyrov/97301801733a31c60521
440
-
441
- https://www.cloudnation.nl/inspiratie/blogs/migrating-heroku-postgresql-to-aurora-rds-with-almost-minimal-downtime
442
-
443
- https://blog.porter.run/migrating-postgres-from-heroku-to-rds/
444
-
445
- https://www.endpointdev.com/blog/2017/06/amazon-aws-upgrades-to-postgres-with/
446
-
447
- https://aws.amazon.com/blogs/database/migrating-legacy-postgresql-databases-to-amazon-rds-or-aurora-postgresql-using-bucardo/
303
+ Download it to protected storage. If your retention destination is S3, use your
304
+ normal AWS credentials and bucket policy to upload the dump. Verify the stored
305
+ backup before removing the source database.