Run the database version the rest of the product already recommends
INSTALL-WINDOWS-IIS.md has said MySQL 8.4 LTS is standard for new installs since 8.0 reached end of life in April 2026, while both compose files and the offline bundler still pinned 8.0. A site reading the Windows runbook and a site reading the Docker one were being told to run different servers, and the migration page written this week sent people onto the dead one. Verified against a real server rather than by editing a tag: 8.4.11, core chain plus five plugin chains applied clean, 66 tables at a single utf8mb4_unicode_ci collation, six alembic version tables. The image's PyMySQL authenticates against 8.4's caching_sha2_password, which is what requirements.in already pins cryptography for. Existing servers need one thing done FIRST: 8.4 removes mysql_native_password, so an account created on 5.6 or 5.7 must be moved to caching_sha2_password before the upgrade or it cannot authenticate afterwards. In-place also has no downgrade path, and 5.7 cannot reach 8.4 in one hop. For databases this size a dump into a fresh 8.4 server is the better trade: same outage, and the old server stays as the rollback. Air-gapped sites need a fresh offline bundle, because the tarball carries the MySQL image alongside the app image. Also here, found by having it bite during that verification: the db healthcheck pinged over the unix socket, and the entrypoint's init pass answers on the socket while running the server with --skip-networking. The probe therefore reported healthy DURING init, which is what `depends_on: service_healthy` gates api and migrate on. A ping passed at 8 seconds and the next query failed because the server was mid-restart. Probing 127.0.0.1 keeps it red until the real server is listening.
This commit is contained in:
22
CHANGELOG.md
22
CHANGELOG.md
@@ -39,6 +39,28 @@ ADR-007 and ADR-002.
|
|||||||
its own hostname, the gate runs BEFORE the manifest is read (the manifest is
|
its own hostname, the gate runs BEFORE the manifest is read (the manifest is
|
||||||
on that share too), and it fails closed on an unparseable date.
|
on that share too), and it fails closed on an unparseable date.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **MySQL 8.4 LTS** in both compose files, and as the default in
|
||||||
|
`build-offline-bundle.ps1`. 8.0 reached end of life in April 2026, which
|
||||||
|
`INSTALL-WINDOWS-IIS.md` already said while the stack still pinned it; the two
|
||||||
|
halves of the product disagreed about which server a site should run.
|
||||||
|
Verified against a real 8.4.11 server rather than by changing a tag: the core
|
||||||
|
chain and five plugin chains applied clean, 66 tables at a single
|
||||||
|
`utf8mb4_unicode_ci` collation, and the image's PyMySQL authenticates against
|
||||||
|
8.4's `caching_sha2_password` (which is why `cryptography` is pinned).
|
||||||
|
Sites upgrading an EXISTING server should note that 8.4 removes
|
||||||
|
`mysql_native_password`: an account created on 5.6 or 5.7 must be moved to
|
||||||
|
`caching_sha2_password` before the upgrade or it cannot log in afterwards.
|
||||||
|
Air-gapped sites need a fresh offline bundle, since the tarball carries the
|
||||||
|
MySQL image.
|
||||||
|
- **The database healthcheck probes over TCP** rather than the unix socket. The
|
||||||
|
entrypoint's init pass answers on the socket while running the server with
|
||||||
|
`--skip-networking`, so a socket ping reported healthy DURING init and
|
||||||
|
`depends_on: service_healthy` released `api` and `migrate` against a server
|
||||||
|
that was about to restart. Found by having it happen: a probe passed at 8s and
|
||||||
|
the next query failed because the server was mid-restart.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- **The image did not build.** `npm run build` fires a `prebuild` hook that runs
|
- **The image did not build.** `npm run build` fires a `prebuild` hook that runs
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ x-app-env: &app-env
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: mysql:8.0
|
image: mysql:8.4
|
||||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
@@ -48,7 +48,12 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${MYSQL_PORT:-3306}:3306"
|
- "127.0.0.1:${MYSQL_PORT:-3306}:3306"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
|
# 127.0.0.1, not localhost: localhost means the unix socket, and the
|
||||||
|
# entrypoint's init pass answers on the socket while running the server
|
||||||
|
# with --skip-networking. A socket ping therefore reports healthy DURING
|
||||||
|
# init, and the api/migrate services start against a server that is about
|
||||||
|
# to restart. Over TCP the probe stays red until the real server listens.
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
image: mysql:8.0
|
image: mysql:8.4
|
||||||
# utf8mb4 server-wide so the auto-created MYSQL_DATABASE is utf8mb4, not the
|
# utf8mb4 server-wide so the auto-created MYSQL_DATABASE is utf8mb4, not the
|
||||||
# image default. Keeps every site's schema on the same charset/collation.
|
# image default. Keeps every site's schema on the same charset/collation.
|
||||||
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci
|
||||||
@@ -36,7 +36,12 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:${MYSQL_PORT:-3306}:3306"
|
- "127.0.0.1:${MYSQL_PORT:-3306}:3306"
|
||||||
healthcheck:
|
healthcheck:
|
||||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
|
# 127.0.0.1, not localhost: localhost means the unix socket, and the
|
||||||
|
# entrypoint's init pass answers on the socket while running the server
|
||||||
|
# with --skip-networking. A socket ping therefore reports healthy DURING
|
||||||
|
# init, and the api/migrate services start against a server that is about
|
||||||
|
# to restart. Over TCP the probe stays red until the real server listens.
|
||||||
|
test: ["CMD", "mysqladmin", "ping", "-h", "127.0.0.1", "-u", "root", "-p${MYSQL_ROOT_PASSWORD}"]
|
||||||
interval: 10s
|
interval: 10s
|
||||||
timeout: 5s
|
timeout: 5s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ pwsh scripts/build-offline-bundle.ps1 -Version 0.7.0
|
|||||||
```
|
```
|
||||||
|
|
||||||
This builds `shopdb-flask:0.7.0` (frontend + all Python deps baked in), pulls
|
This builds `shopdb-flask:0.7.0` (frontend + all Python deps baked in), pulls
|
||||||
`mysql:8.0`, and writes:
|
`mysql:8.4`, and writes:
|
||||||
|
|
||||||
- `shopdb-stack-0.7.0.tar.gz` - both images in one archive
|
- `shopdb-stack-0.7.0.tar.gz` - both images in one archive
|
||||||
- `shopdb-stack-0.7.0.tar.gz.sha256` - checksum to verify after transfer
|
- `shopdb-stack-0.7.0.tar.gz.sha256` - checksum to verify after transfer
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ sane, then use manual for day-to-day work.
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| Python | 3.14 (64-bit) - matches CI, the container image and the Windows installer wheelhouse | `python --version` |
|
| Python | 3.14 (64-bit) - matches CI, the container image and the Windows installer wheelhouse | `python --version` |
|
||||||
| Node.js | 18+ | `node --version` |
|
| Node.js | 18+ | `node --version` |
|
||||||
| MySQL | 8.0 (or Docker, below) | `mysql --version` |
|
| MySQL | 8.4 LTS (or Docker, below) | `mysql --version` |
|
||||||
| Git | any recent | `git --version` |
|
| Git | any recent | `git --version` |
|
||||||
|
|
||||||
On Windows, install all of them with winget (accept each license, then
|
On Windows, install all of them with winget (accept each license, then
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ image with the matching `VITE_BASE_PATH`.
|
|||||||
### 3. MySQL version and character set
|
### 3. MySQL version and character set
|
||||||
|
|
||||||
The Windows runbook supports 5.6, 5.7 and 8.4. `docker-compose.yml` runs
|
The Windows runbook supports 5.6, 5.7 and 8.4. `docker-compose.yml` runs
|
||||||
`mysql:8.0` and forces `utf8mb4` / `utf8mb4_unicode_ci` server-wide so every
|
`mysql:8.4` and forces `utf8mb4` / `utf8mb4_unicode_ci` server-wide so every
|
||||||
site shares one collation.
|
site shares one collation.
|
||||||
|
|
||||||
A dump from an older server can carry `latin1` or 3-byte `utf8` table
|
A dump from an older server can carry `latin1` or 3-byte `utf8` table
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
sets in its .env so docker-compose.airgap.yml runs the matching image.
|
sets in its .env so docker-compose.airgap.yml runs the matching image.
|
||||||
|
|
||||||
.PARAMETER MysqlImage
|
.PARAMETER MysqlImage
|
||||||
MySQL image the stack runs (default mysql:8.0). Must match db.image in
|
MySQL image the stack runs (default mysql:8.4). Must match db.image in
|
||||||
docker-compose.airgap.yml.
|
docker-compose.airgap.yml.
|
||||||
|
|
||||||
.PARAMETER OutDir
|
.PARAMETER OutDir
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
[CmdletBinding()]
|
[CmdletBinding()]
|
||||||
param(
|
param(
|
||||||
[string]$Version = '0.7.0',
|
[string]$Version = '0.7.0',
|
||||||
[string]$MysqlImage = 'mysql:8.0',
|
[string]$MysqlImage = 'mysql:8.4',
|
||||||
[string]$OutDir = '.',
|
[string]$OutDir = '.',
|
||||||
[switch]$SkipBuild
|
[switch]$SkipBuild
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user