Skip to content

feat(database): add "database mysql upgrade" command#2005

Draft
martin-helmich wants to merge 2 commits into
masterfrom
feat/database-mysql-upgrade
Draft

feat(database): add "database mysql upgrade" command#2005
martin-helmich wants to merge 2 commits into
masterfrom
feat/database-mysql-upgrade

Conversation

@martin-helmich

@martin-helmich martin-helmich commented Jul 15, 2026

Copy link
Copy Markdown
Member

Adds a database mysql upgrade command for upgrading a MySQL database to a newer version.

Background

The API has no dedicated upgrade operation. PATCH /v2/mysql-databases/{id} accepts an optional version field alongside description and characterSettings, and that is what this command drives.

Behaviour

  • Upgrade candidates come from GET /v2/mysql-versions, filtered to enabled versions newer than the database's current one. MySQL does not support downgrades, so older versions are never offered.
  • Versions are compared numerically via semver coercion. The <major>.<minor> format is not valid semver, and a lexical sort would place 10.11 before 8.0.
  • --version takes an explicit version or latest; if omitted, the version is prompted for interactively, following app upgrade.
  • If the database already runs the newest available version, the command reports that and exits without patching.
  • An invalid --version fails with the list of valid targets rather than sending a doomed request.
  • Upgrades are confirmed unless --force is passed, since they are disruptive.
  • --wait polls until the upgrade has actually completed (see below).

Notes for reviewers

The interesting part is --wait. Two API behaviours make the obvious implementation wrong, both found by testing against a real database:

  • version holds the desired version and is updated as soon as the patch is accepted, so it does not indicate that the upgrade finished.
  • status still reads ready from before the upgrade for a moment after patching.

Together these mean a naive "target version and ready" check returns before the upgrade even starts, leaving the database at pending. Unlike AppAppInstallation, DatabaseMySqlDatabase has no current/desired pair, so the current == desired approach used by waitUntilAppStateHasNormalized is not available here.

Instead, statusSetAt is captured before patching and required to have changed before ready is trusted, proving the database changed state at least once since. Both timestamps come from the API, so the comparison is immune to client clock skew. This is isUpgradeComplete() in lib/resources/database/mysql/upgrade.ts, covered by unit tests including the stale-ready regression.

The existing command tests in this area are all it.skip'd ("to be fixed later"), so rather than add another skipped test, the version-selection and completion logic are extracted into lib/resources/database/mysql/ and covered by real unit tests.

🤖 Generated with Claude Code

Adds a command to upgrade a MySQL database to a newer version, backed by
the PATCH /v2/mysql-databases/{id} endpoint, which accepts a version field.

Upgrade candidates are determined from the mysql-versions endpoint, keeping
only enabled versions newer than the one the database currently runs; MySQL
does not support downgrades. Versions are compared numerically via semver
coercion, since the "<major>.<minor>" format is not valid semver and would
otherwise sort lexically (e.g. "10.11" < "8.0").

The target version can be given explicitly, set to "latest", or selected
interactively. Upgrades are confirmed unless --force is passed, and --wait
polls until the database reports the target version and is ready again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@martin-helmich martin-helmich marked this pull request as draft July 15, 2026 12:57
The --wait flag returned immediately, leaving the database at status
"pending". Patching the version updates the database's `version` field
right away, as it holds the desired version rather than the running one,
and the status still reads "ready" from before the upgrade for a moment
after patching. The first poll therefore observed the target version in a
"ready" database and considered the upgrade done before it had started.

Capture `statusSetAt` before patching and require it to have changed, which
proves the database has changed state at least once since. Both timestamps
originate from the API, so comparing them is not affected by client clock
skew.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
martin-helmich added a commit that referenced this pull request Jul 15, 2026
)

`database mysql list` displayed `pending` for a database that the API
reported as `migrating`.

## Cause

The status column did not use the `status` field returned by the API. It
derived the value from the `isReady` boolean instead:

```ts
get: (row) => {
  if (!row.isReady) {
    return "pending";
  }
  return "ready";
},
```

`DatabaseDatabaseStatus` has five values — `pending`, `ready`,
`migrating`, `importing` and `error` — so this collapsed everything that
was not ready into `pending`. Besides `migrating`, this also affected
`error`: a failed database was indistinguishable from one that is still
being worked on.

The same code existed in `database list`, which had a second problem.
`DatabaseRedisDatabase` has no `isReady` field, so Redis rows were
hardcoded to `isReady: true` and always displayed as `ready`, whatever
their actual state.

## Fix

Both MySQL and Redis databases report `status`, so the column now uses
it directly and the Redis `isReady` hardcode is gone. Since the column
key already matches the field, the custom getter is no longer needed.

Verified against a mock serving all five states; each is now reported
verbatim, and a pending Redis database no longer shows as ready.

Found while testing #2005, where an upgrade puts the database into
`migrating`, but this is an independent pre-existing bug, hence the
separate PR.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
mittwald-machine added a commit that referenced this pull request Jul 16, 2026
## [1.20.3](v1.20.2...v1.20.3) (2026-07-16)

### Bug Fixes

* **database:** report the actual database status in list commands ([#2006](#2006)) ([2b17ba6](2b17ba6)), closes [#2005](#2005)
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.

1 participant