Files
twenty/packages/twenty-server
Charles Bochet 577b22df46 fix(upgrade): invalidate upgrade-status cache on command end (#21497)
## Problem

The "Twenty / Upgrade Status" Grafana dashboard shows stale workspace
counts (e.g. `N behind / 0 up-to-date` while the instance reads
`UP_TO_DATE`) that disagree with `command:prod upgrade:status`. The CLI
is correct; the dashboard lags, sometimes for the full hour.

## Root cause

The dashboard is fed by the `twenty_upgrade_workspaces_*` gauges, which
read their workspace counts from a Redis snapshot
(`UpgradeStatusCacheService`). That snapshot is only invalidated
**per-command, inside the runners' `finally` blocks**. Two gaps:

1. An instance command that is already applied returns **before** its
invalidation runs (`isAlreadyCompleted` early-return in
`InstanceCommandRunnerService`). So a plain **redeploy** — which changes
the deployed upgrade sequence, and thus the "behind" answer, without
executing any command — never refreshes the snapshot. This is most
visible on an instance-only release.
2. The snapshot then stays frozen until its 60-minute TTL, while the CLI
reads live and disagrees.

"Behind" is derived from the deployed sequence, not just the ledger, so
the correct answer changes on events (deploys) that run no command —
which is exactly why per-command invalidation isn't enough on its own.

## Fix

Invalidate the upgrade-status cache **once, unconditionally, at the end
of both upgrade entrypoints** — `run-instance-commands` (the
deploy/migrate step) and `upgrade` — in a `finally`. Every run,
including a no-op redeploy where all commands are already applied, now
clears the snapshot, so the next gauge scrape recomputes against the
current sequence. Best-effort (failures are logged, never block the
command). The existing per-command invalidation is kept for mid-run
progress.

This keeps the read path untouched.

## Reproduction + verification (live, local)

Served twenty-server (`NODE_PORT=4000`, `METER_DRIVER=prometheus`)
against the seeded DB, whose latest version `2.12.0` is instance-only.

1. Froze the gauge at `behind 4 / up_to_date 0` while the DB was brought
up-to-date (snapshot not invalidated) — reproduced the dashboard/CLI
divergence.
2. Ran the **patched** `run-instance-commands --force`. Every step
logged `already executed, skipping` — and the `finally` still deleted
the Redis snapshot.
3. On the next recompute the gauge self-healed to `instance_health 1,
behind 0, up_to_date 4`, matching the live CLI.

With the old code the snapshot stayed frozen at `behind 4` until the
TTL.
2026-06-12 16:01:59 +00:00
..