Skip to content

chore: delete retired guests, refresh the schema snapshot, document non-rolling upgrades - #75

Merged
darwin67 merged 4 commits into
mainfrom
chore/schema-housekeeping
Oct 11, 2026
Merged

darwin67 merged 4 commits into
mainfrom
chore/schema-housekeeping

Conversation

@darwin67

Copy link
Copy Markdown
Member

Summary

Three follow-ups left over from the design stack, #63–#69.

1. Delete retired guest users (20261012090000)
RetireGuestUsers hid guest content and left the user rows "until a later cleanup". This migration is that cleanup.

  • Deleting a guest user cascades to its personal organization, workspace, memberships and tokens.
  • Pastes block workspace deletion, so they are removed first.
    • Inline pastes live only in Postgres. The migration deletes them, which is all the cleaner would do for them.
    • Pastes with a storage key have an object the migration can't reach. They stay for the cleaner, and their guest is skipped.
  • Where the cleaner has run since the retirement migration, nothing is skipped. It purges marked workspaces every 15 minutes.
  • Can't be undone.

2. Regenerate priv/repo/structure.sql on PostgreSQL 17
The snapshot hadn't been dumped since #59, so it was missing every migration from #63 on. I regenerated it with mix ecto.migration against PG 17.11, the major version CI runs. One change isn't from a migration: pending_uploads now lists claimed_at before inserted_at, which is the order its migration creates. The column types are the same.

3. Upgrade guidance in docs/self-hosting.md
The upgrade guide promised that every migration was safe during a rolling deploy. Two aren't, and each instance runs its own cleaner, so one old instance left running is enough to cause trouble:

  • Paste lifecycle: older code treats a paste as live unless it has expired. It would show deleted pastes, and its cleaner would hard-delete expired pastes that are still restorable.
  • Guest retirement: older code still creates guest accounts, and any created afterwards would never be retired.

The guide now says to stop every old instance before running /app/bin/migrate for these two. It also notes which migrations can't be undone.

Deploying this to an existing install

The safe sequence:

  1. The lifecycle and guest-retirement migrations are deployed with old instances stopped first.
  2. The new cleaner has run at least once.
  3. This PR is deployed with a normal rollout.

Test plan

  • Migration test covers three accounts:
    • A guest with an inline paste is deleted, along with its organization and workspace.
    • A guest with a stored paste is kept, and so is the paste.
    • A registered account is untouched.
  • mix test: 564 tests, 0 failures. mix credo reports no issues. mix compile --warnings-as-errors is clean.
  • The snapshot was generated by running every migration on a fresh PostgreSQL 17 database.

🤖 Generated with Claude Code

https://claude.ai/code/session_019fnjMN7WpEjQJD2zveakV6


Generated by Claude Code

RetireGuestUsers hid guest content and marked each guest's personal
workspace for deletion, leaving the user rows "until a later cleanup".
This is that cleanup. Deleting a guest user cascades to its personal
organization, workspace, memberships and tokens.

Pastes restrict workspace deletion, so they go first:

- Inline pastes live only in PostgreSQL, so the migration deletes them,
  which is all the cleaner would do for them.
- Pastes with a storage key also have an object in local or S3 storage
  that a migration cannot reach. They are left to the cleaner, and their
  guest is skipped.

On an install where the cleaner has run since RetireGuestUsers (it purges
marked workspaces every 15 minutes by default) nothing is skipped. On an
install that applies both migrations in one go, only guests with stored
pastes remain, as inert rows; the admin "Guest" status and test fixture
stay for them.

The migration test shares its database setup and covers a guest with an
inline paste (deleted), a guest with a stored paste (kept) and a
registered account (untouched).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019fnjMN7WpEjQJD2zveakV6
priv/repo/structure.sql had not been dumped since #59, so it was missing
every migration from #63 onward: paste title and reference, last-used
workspace, the removal lifecycle and recovery window, guest retirement,
appearance, and the guest deletion migration. Regenerated with
`mix ecto.migration` against PostgreSQL 17.11, the major version CI runs
and the one the previous snapshot came from.

pending_uploads now lists claimed_at before inserted_at with plain
`timestamp`, which is what its migration creates (both columns are
:utc_datetime_usec); the old snapshot's order and explicit precision did
not come from these migrations. The types are identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019fnjMN7WpEjQJD2zveakV6
The upgrade procedure promises migrations are safe under a rolling
deployment. Two recent ones are not, and the web app and its background
cleaner run in the same instance, so one old instance left running is
enough:

- The paste lifecycle migration. Older code treats a paste as live
  unless it has expired, so it would show pastes deleted under the new
  code, and its cleaner would hard-delete expired pastes that are still
  restorable.
- The guest retirement migration. Older code still creates guest
  accounts, and those would never be retired.

Also note that the two guest migrations cannot be undone, and what is
left behind when an install applies both at once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019fnjMN7WpEjQJD2zveakV6
@github-actions github-actions Bot added the chore label Oct 11, 2026
Remove conflicting rolling-deployment guidance for retirement. Limit rolling guest deletion to guest-free lifecycle-aware instances, and explain that skipped user rows require later cleanup. Exercise migrations from before retirement and assert storage-backed guests remain retired/token-free while registered accounts are unchanged. 564 tests and Credo pass.
@darwin67

darwin67 commented Oct 11, 2026 •

Copy link
Copy Markdown
Member Author

Oracle review complete: signed off after correcting contradictory upgrade guidance. Guest retirement still requires stopping all old instances. The later guest-deletion migration supports normal rolling deployment only when all running instances already use guest-free, lifecycle-aware code. Clarified that storage-backed guests skipped by the migration remain pending a later user cleanup even after the cleaner purges their pastes. Strengthened the migration test: fixtures now predate retirement, retained guests have deletion markers/expired pastes/revoked tokens, and registered accounts remain unaffected. No migration SQL or schema snapshot changes were required by review. Validation: mix precommit (564 ExUnit tests plus JS tests) and mix credo passed. Corrections committed and pushed without rewriting published history. No production migration or deployment was triggered. Review session: https://ampcode.com/threads/T-01a12594-e2f1-75be-b343-9239564f5c84

Post-push CI is green: memory/local/S3 Elixir tests, Rust tests, formatting/linting, and production-container build all passed. Release/publication jobs skipped as expected.

@darwin67
darwin67 marked this pull request as ready for review October 11, 2026 04:40
@darwin67
darwin67 merged commit d4271fd into main Oct 11, 2026
17 checks passed
@darwin67
darwin67 deleted the chore/schema-housekeeping branch October 11, 2026 04:40
@chaba2-bot chaba2-bot Bot mentioned this pull request Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants