Skip to content

App migration fix - #216

Merged
calebyhan merged 2 commits into
mainfrom
fix/app-migration-fix
Aug 26, 2026
Merged

App migration fix#216
calebyhan merged 2 commits into
mainfrom
fix/app-migration-fix

Conversation

@calebyhan

Copy link
Copy Markdown
Contributor

Fixes deploy downtime and a related outage caused by the backend/frontend
Deployments using Recreate instead of RollingUpdate. Traced to a real
incident: a rollout hit a node DiskPressure condition, and the uploads
PVC being ReadWriteOnce turned that into ~29 pods stuck in
ContainerStatusUnknown and extended downtime instead of a quick blip.

Changes:

  • Switched the uploads PVC to ReadWriteMany (RWX) so a rolling update's
    surge pod can mount it alongside the still-terminating old pod, instead
    of hitting a Multi-Attach error. Added
    migrate-uploads-to-rwx.sh since accessModes is immutable on an
    existing bound PVC — this needs to be run once, live, before this
    template is applied.
  • Switched both senate-backend and senate-frontend Deployments from
    Recreate to RollingUpdate (maxSurge: 1, maxUnavailable: 0), so a
    deploy never has a window with zero pods serving traffic.
  • Trimmed BACKEND_MEMORY_LIMIT (512Mi→256Mi) and FRONTEND_MEMORY_LIMIT
    (1Gi→896Mi) based on live oc adm top pods usage (~85Mi and ~550Mi
    respectively) — frees just enough of the project's fixed 2.5Gi memory
    quota for a frontend surge pod to fit too, no UNC IT quota increase
    needed. db's limit is deliberately untouched.
  • Added a startupProbe (polls every 2s instead of a flat 20s
    initialDelaySeconds) and a preStop hook (lets the Service/Route stop
    routing before SIGTERM) to both app Deployments.
  • Documented the storage/memory quota math, the destructive-schema-change
    playbook (init_db.py is additive-only by design — drops/renames/
    required-no-default columns need manual expand-contract handling), and
    the Multi-Attach incident in deploy/cloudapps/README.md.

The backend and frontend Deployments used strategy: Recreate, which stops
the old pod before starting the new one, causing a real gap in the middle
of every deploy. This traced to a live incident: a RollingUpdate surge pod
would fail to mount the ReadWriteOnce uploads PVC while the old pod still
held it, which combined with a node hitting DiskPressure during the last
backend rollout to strand ~29 pods in ContainerStatusUnknown and extend
what should have been a brief blip into real downtime.

- senate-uploads PVC switches to ReadWriteMany (the ontap-nas-economy
  backing storage class supports it); migrate-uploads-to-rwx.sh handles
  the one-time swap since accessModes is immutable on an existing PVC.
- senate-backend Deployment switches to RollingUpdate (maxSurge: 1,
  maxUnavailable: 0), which fits the project's memory quota exactly.
- senate-frontend stays on Recreate for now - a surge pod needs another
  1Gi the quota doesn't have room for - documented in the README along
  with what to ask UNC IT for.
- Both Deployments get a startupProbe (shrinks the Recreate/rollout gap
  by not hiding a fast boot behind a flat initialDelaySeconds) and a
  preStop hook (gives the Service/Route time to stop routing to a pod
  before it gets SIGTERM).
- README documents the additive-only init_db.py migration approach and
  the manual expand/contract playbook destructive schema changes still need.
Checked live usage with oc adm top pods: backend and DB were using
~85-95Mi against 512Mi limits each, frontend ~550Mi against 1Gi. That's
a lot of committed-but-idle quota, all of it counted against the
project's 2.5Gi hard cap regardless of actual use.

Lowering BACKEND_MEMORY_LIMIT to 256Mi (still ~3x measured usage) and
FRONTEND_MEMORY_LIMIT to 896Mi (~1.6x measured peak) frees exactly enough
room for a frontend surge pod to fit too - 512(db) + 256(backend) +
896(frontend) = 1664Mi steady, + up to 896Mi surge = 2560Mi, the exact
cap. db's limit is left untouched on purpose: a Postgres OOM-kill is a
worse failure mode than a slow web process.

senate-frontend switches to RollingUpdate (maxSurge: 1, maxUnavailable: 0)
now that it fits, closing the last remaining downtime window on ordinary
deploys without waiting on a UNC IT quota increase. Zero slack is left
in the quota either way, which the README now documents along with what
that means if frontend and backend rollouts ever overlap.
@github-actions

Copy link
Copy Markdown

Test Results

589 tests  ±0   589 ✅ ±0   52s ⏱️ ±0s
  1 suites ±0     0 💤 ±0 
  1 files   ±0     0 ❌ ±0 

Results for commit 56dacb8. ± Comparison against base commit 45e7c23.

@MasonMines2006 MasonMines2006 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@MasonMines2006 MasonMines2006 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@calebyhan
calebyhan merged commit 512a860 into main Aug 26, 2026
3 checks passed
@calebyhan
calebyhan deleted the fix/app-migration-fix branch August 26, 2026 15:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants