Files
ftb-project-management/docs/runbooks/appdata-retirement.md
72a59f125c docs(ops): 补齐生产 runbook 和 readiness 清单
- 新增迁移回滚、AppData 退场、小宝后台任务 runbook\n- 新增生产 readiness 证据清单和 runbook placeholder 扫描\n- 更新部署文档与路线图到 V2.8 运维闭环阶段\n\nCo-Authored-By: GPT-5 Codex <codex@openai.com>
2026-07-08 16:28:02 +08:00

3.9 KiB

AppData Retirement Runbook

Use this when retiring an AppData key after its domain writes have moved to relation-table APIs. The order is fixed: backup, measure, freeze writes, compare, remove fallback, archive.

Scope Gate

Retire one AppData key family at a time. Good candidates have domain CRUD writes, read APIs, pagination boundaries, audit events, and a rollback path.

Preparation

pnpm backup:postgres -- --env-file .env.production
pnpm backup:server-data -- --env-file .env.production
pnpm deploy:smoke -- --base-url http://localhost

Record the key family being retired, the owning domain API, and the relation tables that replace it.

Count And Consistency Checks

Run counts before disabling writes.

docker compose --env-file .env.production -f docker-compose.prod.yml exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "select key, jsonb_array_length(value) as appdata_rows from app_data where key in ('products-overview','requirements','version-plans','dev-tasks','test-cases','bugs','members','task-categories','task-worklogs','overtime') order by key;"
docker compose --env-file .env.production -f docker-compose.prod.yml exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "select 'requirements' as table_name, count(*) from requirements union all select 'version_plans', count(*) from version_plans union all select 'dev_tasks', count(*) from dev_tasks union all select 'test_cases', count(*) from test_cases union all select 'bugs', count(*) from bugs;"

For partitioned entities, also check missing partition keys.

docker compose --env-file .env.production -f docker-compose.prod.yml exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "select 'requirements_missing_product' as check_name, count(*) from requirements where product_id is null union all select 'dev_tasks_missing_version', count(*) from dev_tasks where version_id is null union all select 'test_cases_missing_version', count(*) from test_cases where version_id is null union all select 'bugs_missing_version', count(*) from bugs where version_id is null;"

Disable AppData Writes

  1. Merge the domain-specific frontend store change that stops calling saveServerData for the retired key.
  2. Keep AppData read fallback for one release while relation reads are verified.
  3. Deploy and run smoke tests.
pnpm deploy:smoke -- --base-url http://localhost --expected-version "$APP_VERSION"

Remove Fallback

Remove AppData read fallback only after one successful release where:

  • Domain writes went through relation APIs.
  • AppData row counts did not grow for the retired key.
  • V2.2 read paths and page workflows returned expected data.
  • No AppData relation sync failed logs appeared during the observation window.

Archive

Export retired keys before any later table cleanup.

docker compose --env-file .env.production -f docker-compose.prod.yml exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -c "copy (select key, value, updated_at from app_data where key in ('dev-tasks','test-cases','bugs')) to stdout with csv header" > backups/postgres/appdata-retired-executions-20260708.csv

Rollback

  • If relation writes fail but AppData still has fresh data, roll back to the previous app image that still writes AppData.
  • If AppData writes were already disabled and relation writes are bad, restore PostgreSQL from the backup made at the start of this runbook.
  • If only read fallback removal caused the issue, roll back app images first and leave the database unchanged.

Data Risks

  • Removing fallback too early can hide valid historical JSON rows that were never mapped into relation tables.
  • Re-enabling old AppData writes after relation writes have accepted new edits can overwrite newer relation state through compatibility sync.
  • AppData exports can contain business-sensitive text; store backup CSV files in the same restricted location as database dumps.