openvibers/openvibe.community Commits

Add rehearsal, backup, verification and rollback to the 0005 cutover…

OP OpenVibers committed 4 Oct 2026 · a522be9e3e · 1 file, +50 −14

Requested by finish-f1. Handed to the local harness by the cloud job pr-OpenVibe.Community-15; done by Claude sonnet.

Changes

ddiff --git a/docs/cutover-community-0005-space-moderators.md b/docs/cutover-community-0005-space-moderators.md
iindex d44f1e3..c4c3212 100644
---- a/docs/cutover-community-0005-space-moderators.md
++++ b/docs/cutover-community-0005-space-moderators.md
@@@ -16,20 +16,45 @@ changed, and the previous release never reads the table.
## 0. Rules
--- Ship it with the normal pipeline (`ov deploy OpenVibe.Community`). It needs no window.
++- This is the one runbook of PR #15. Ship it with the normal pipeline (`ov deploy OpenVibe.Community`).
++ It needs no window.
- N-1: the previous release runs against the new schema throughout the mixed-version window. It
ignores the table, so its moderation is still global-only until every process runs the new release.
++- Order: rehearsal (section 1) → backup (section 2) → deploy and verify (section 3).
## 1. Rehearsal
--1. Restore a recent copy of `ov_community` into a scratch database (the restore-drill copy will do).
--2. Boot this release against it (`DATABASE_URL`/`DATABASE_DIRECT_URL` pointing at the copy), or run
-- `npm run test:pg` with the copy as the test store. Check that `ov_migrations` lists `0005` and that
-- `\d space_moderators` shows the primary key `(space_id, subject_id)`, the foreign key to `spaces`
-- (`ON DELETE CASCADE`) and `idx_space_moderators_subject`.
--3. Run the moderator tests: `ov test space-moderators forum members-only security-idor chat-room`.
++The harness runs `ov rehearse OpenVibe.Community 15` on a scratch PostgreSQL: main's migrations, the
++repository's fixtures, this PR's migrations (a second run must apply nothing), then the commands below,
++each with `DATABASE_URL` on that database. They check the table's columns, the index, and that deleting
++a space removes its moderator rows.
--## 2. Cutover
++```rehearse
++psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -tAc "SELECT count(*) FROM information_schema.columns WHERE table_name = 'space_moderators'" | grep -qx 4
++psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -tAc "SELECT count(*) FROM pg_indexes WHERE indexname = 'idx_space_moderators_subject'" | grep -qx 1
++psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -tAc "SELECT count(*) FROM space_moderators" | grep -qx 0
++psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -tAc "WITH s AS (INSERT INTO spaces (slug, name) VALUES ('rehearse-moderators', 'Rehearsal') RETURNING id), m AS (INSERT INTO space_moderators (space_id, subject_id, added_by) SELECT id, 'usr_rehearse', 'usr_staff' FROM s RETURNING 1) SELECT count(*) FROM m" | grep -qx 1
++psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -c "DELETE FROM spaces WHERE slug = 'rehearse-moderators'"
++psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -tAc "SELECT count(*) FROM space_moderators" | grep -qx 0
++```
++
++Beyond the harness, against a restored copy of `ov_community` (the restore-drill copy), run
++`ov test space-moderators forum members-only security-idor chat-room`, or `npm run test:pg`.
++
++## 2. Backup
++
++Before the deploy, take a custom-format dump of the production database and keep it until the N-1 window
++has closed:
++
++```
++pg_dump --format=custom --file=ov_community-pre-0005.dump "$DATABASE_DIRECT_URL"
++pg_restore --list ov_community-pre-0005.dump | head
++```
++
++The migration only adds a table, so the dump is the safety net for the whole database, not for a
++particular row change.
++
++## 3. Cutover
1. Deploy. The migration runs at the first boot of the new release.
2. **Verify:**
@@@ -38,15 +63,26 @@ changed, and the previous release never reads the table.
applied and no migrate error.
- `curl -s https://openvibe.community/api/v1/spaces/general/moderators` answers
`{ "space": "general", "moderators": [] }`.
++ - `psql "$DATABASE_DIRECT_URL" -c '\d space_moderators'` shows the primary key
++ `(space_id, subject_id)`, the foreign key to `spaces` (`ON DELETE CASCADE`) and
++ `idx_space_moderators_subject`.
++
++## 4. Rollback and restore
++
++Code rollback (the normal way back): redeploy the previous release and leave the table where it is. The
++previous release ignores it. Rows added in the meantime stay, and they apply again when the release comes
++back. The migration is never reverted or edited (`ov_migrations` keeps its checksum).
--## 3. Rollback
++Undoing the schema by hand (only if the table itself is the problem): redeploy the previous release
++first, then `DROP TABLE space_moderators;` (the index goes with it). Otherwise a later contract
++migration drops the table after the N-1 window.
--Leave the table where it is. A rollback to the previous release ignores it. Rows added in the meantime
--stay, and they apply again when the release comes back. The migration is never reverted or edited
--(`ov_migrations` keeps its checksum). If it must be undone by hand, a later contract migration drops the
--table after the N-1 window.
++Restore (only if the database is damaged, which this migration cannot cause): stop the service,
++`pg_restore --clean --if-exists --dbname="$DATABASE_DIRECT_URL" ov_community-pre-0005.dump`, start the
++previous release, and repeat the health and journal checks. Moderators added after the backup are lost by
++a restore, so prefer the code rollback.
--## 4. Afterwards
++## 5. Afterwards
- Account deletion erases a person's rows, and `added_by` becomes NULL where they added someone. A
subject merge moves the rows (`server/identity/account-data.js`, `subject-merge.js`).