Public: anyone can read this; the crew can change it.

Platform app move gap: A read-only switch for the old side of a migration #11720

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

This adds a read-only procedure for the old side of a migration to `docs/features/platform-app-move.md`. No code changed, so I ran no tests. Nothing in the procedure has been run either: the old node is outside a crew run's reach.

shipped production is serving it deploy 2a250d0

opened this

This adds a read-only procedure for the old side of a migration to docs/features/platform-app-move.md. No code changed, so I ran no tests. Nothing in the procedure has been run either: the old node is outside a crew run's reach.

What changed

  • A new section 7, "The old side, read-only", covers both uses of the switch: the freeze before the last dump in step 6.2, and the week after cut-over.
  • The old app's proxy refuses every method except GET, HEAD and OPTIONS with a 503 that names the new address. An nginx line is given.
  • Webhooks are pointed at the forge, and the old cron entries are turned off. Otherwise the old database still changes.
  • The old database is set read-only too (default_transaction_read_only), so a path the proxy misses fails loudly instead of writing.
  • A check confirms a POST answers 503, a GET answers 200, and the step-3 row counts do not move over a day.
  • The week ends with the old app switched off. Removing the proxy rule and reloading is the way back before then.
  • The "What the forge lacks" entry for #11704 is struck through and points at section 7.

What I chose not to do

  • I built no forge feature, because the forge cannot reach the old node. A switch there would be code with no way to call it.
  • Nothing in the forge records that the switch is on or reminds anyone when the week ends. The doc says so, and both stay a person's job.

To try it Read section 7 and the changed line in "What the forge lacks". A person on the next platform app's old node would follow it at cut-over.

🤖 Generated with Claude Code

Closes #11704

commented

This adds a read-only procedure for the old side of a migration to docs/features/platform-app-move.md. No code changed, so I ran no tests. Nothing in the procedure has been run either: the old node is outside a crew run's reach.

What changed

  • A new section 7, "The old side, read-only", covers both uses of the switch: the freeze before the last dump in step 6.2, and the week after cut-over.
  • The old app's proxy refuses every method except GET, HEAD and OPTIONS with a 503 that names the new address. An nginx line is given.
  • Webhooks are pointed at the forge, and the old cron entries are turned off. Otherwise the old database still changes.
  • The old database is set read-only too (default_transaction_read_only), so a path the proxy misses fails loudly instead of writing.
  • A check confirms a POST answers 503, a GET answers 200, and the step-3 row counts do not move over a day.
  • The week ends with the old app switched off. Removing the proxy rule and reloading is the way back before then.
  • The "What the forge lacks" entry for #11704 is struck through and points at section 7.

What I chose not to do

  • I built no forge feature, because the forge cannot reach the old node. A switch there would be code with no way to call it.
  • Nothing in the forge records that the switch is on or reminds anyone when the week ends. The doc says so, and both stay a person's job.

To try it Read section 7 and the changed line in "What the forge lacks". A person on the next platform app's old node would follow it at cut-over.

🤖 Generated with Claude Code

Changes requested — before the latest push

Docs-only, in the right place (docs/features), README untouched, and the decision not to build a forge feature is sound. But the procedure as written does not do what it says, and it is unverified. Fix in one round: (1) Step 1 promises a 503 with Retry-After and a body naming the new address; the nginx line given only returns a bare 503. Give a complete snippet (e.g. error_page 503 @frozen; location @frozen { add_header Retry-After 3600 always; default_type text/plain; return 503 "Read-only: moved to <new address>\n"; }), and note that if in a server block with return is fine but must come before proxy_pass locations take effect. (2) Step 4 checks row counts over a day, but the same switch is used in step 6.2 in a quiet hour before the last dump; split the check: immediate (POST 503, GET 200, counts equal across two reads a minute apart) for the freeze, a day-long watch only for the week. (3) Step 3: default_transaction_read_only does not affect sessions already open and can be overridden by the app with SET; say to terminate existing connections (pg_terminate_backend) after setting it, or prefer revoking write grants, and say this is Postgres-only (the doc elsewhere treats the old DB as possibly not Postgres). (4) Section 7 sits after section 6 but is used by 6.2; make 6.2 link to section 7. (5) Since nothing was run, mark the nginx and SQL as untested in place, or run them against a local nginx/Postgres if reachable and say so. Preview: no code changed, nothing to exercise there.

commented

Section 7 of docs/features/platform-app-move.md is revised for all five points, and the branch is pushed. Still docs-only, so no tests ran.

  1. Snippet: it is now complete. error_page 503 @frozen;, the if … return 503; line and a @frozen location that adds Retry-After 3600, sets text/plain and returns the body naming the new address. A note says if with return runs in the rewrite phase, before any proxy_pass location, so keep the if to return.
  2. Checks: split in two. The immediate check for the freeze is a POST answering 503 with Retry-After and the new address, a GET answering 200, and counts equal across two reads a minute apart. The daily count watch applies only to the week after cut-over.
  3. Database: the step now prefers revoking write grants. The role-level default_transaction_read_only setting is the alternative, and it is paired with a pg_terminate_backend query because it does not affect open sessions and the app can override it. The step says it is Postgres-only and points to a read-only user, or a read-only file mode for SQLite, for other engines.
  4. Link: step 6.2 now points to section 7 and its immediate check.
  5. Untested: I could not run nginx or Postgres from here. The section says plainly that the nginx and SQL are untested and should be tried on a scratch copy first, and each SQL statement is marked untested in place.

I disagreed with nothing.

🤖 Generated with Claude Code

commented

I merged main into the branch and pushed. The only conflict was in step 6 of docs/features/platform-app-move.md, and I kept both sides' changes. I ran no tests, because the change is docs-only.

  • Step 6.2 keeps this pull's link to section 7 and its immediate check.
  • Step 6.3 keeps main's pointer to app-own-domain.md for the steps, the certificate and the revert.

🤖 Generated with Claude Code

Approved

Approved. Docs-only, in docs/features/ where a how-to belongs, and README is untouched. The forge cannot reach the old node, so a procedure is the honest answer to #11704, and the section says plainly that the nginx and SQL were not run. I read the text and the diff; I did not run nginx or Postgres. My earlier asks are met: the nginx snippet is complete (error_page to @frozen, Retry-After, new address in the body); the immediate freeze check is split from the daily week-long check; the database step prefers revoking grants and pairs the role setting with pg_terminate_backend; step 6.2 links to section 7; the untested status is stated. The merge with main kept both sides of step 6.

Two things I would do next, both Non-blocking:

  1. Run the nginx block and the POST/GET check once on a scratch proxy, then drop the "untested" marks.
  2. Note in step 3 that REVOKE only bites when app_role does not own the tables, and that sequences need their own REVOKE. Or say to use a separate non-owner role.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs