Report PostgreSQL collation drift in live smoke
This commit is contained in:
@@ -7,6 +7,14 @@
|
|||||||
|
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## Sprint 36 PostgreSQL collation maintenance visibility (2026-06-17)
|
||||||
|
|
||||||
|
- Added database collation version reporting to `scripts/live_migration_smoke.sh`.
|
||||||
|
- The live smoke now prints `COLLATION_VERSION_MISMATCH` plus the exact `ALTER DATABASE ... REFRESH COLLATION VERSION` acknowledgement command when an old PostGIS volume is reused on a newer runtime.
|
||||||
|
- Documented the Unraid maintenance procedure and backup/index review guidance.
|
||||||
|
- Added regression coverage for the collation mismatch reporting path.
|
||||||
|
- No API contracts, migrations, product features, provider fetching or AI behavior were introduced.
|
||||||
|
|
||||||
## Sprint 35 Docker runtime secret hygiene (2026-06-17)
|
## Sprint 35 Docker runtime secret hygiene (2026-06-17)
|
||||||
|
|
||||||
- Removed embedded PostGIS database name/user/password defaults from `deploy/unraid/Dockerfile.all-in-one` image metadata.
|
- Removed embedded PostGIS database name/user/password defaults from `deploy/unraid/Dockerfile.all-in-one` image metadata.
|
||||||
|
|||||||
@@ -22,3 +22,13 @@ def test_live_migration_smoke_checks_required_runtime_schema_objects() -> None:
|
|||||||
assert '"public.detections"' in content
|
assert '"public.detections"' in content
|
||||||
assert '"public.segmentations"' in content
|
assert '"public.segmentations"' in content
|
||||||
assert '"public.ix_segmentations_geometry"' in content
|
assert '"public.ix_segmentations_geometry"' in content
|
||||||
|
|
||||||
|
|
||||||
|
def test_live_migration_smoke_reports_collation_version_mismatch_without_failing() -> None:
|
||||||
|
script = Path(__file__).resolve().parents[2] / "scripts" / "live_migration_smoke.sh"
|
||||||
|
content = script.read_text(encoding="utf-8")
|
||||||
|
|
||||||
|
assert "pg_database_collation_actual_version(oid)" in content
|
||||||
|
assert "COLLATION_VERSION_MISMATCH" in content
|
||||||
|
assert "REFRESH COLLATION VERSION" in content
|
||||||
|
assert "Database collation version: ok" in content
|
||||||
|
|||||||
@@ -91,6 +91,22 @@ curl -I "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/geointel-icon.svg
|
|||||||
curl -I "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/geointel-icon.png"
|
curl -I "http://192.168.10.150:${GEOINTEL_FRONTEND_PORT:-1202}/geointel-icon.png"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
The live migration smoke also checks PostgreSQL database collation metadata.
|
||||||
|
When reusing a PostGIS volume created by an older Debian/glibc runtime, it may
|
||||||
|
print `COLLATION_VERSION_MISMATCH`. This is a maintenance warning, not an app
|
||||||
|
startup failure. Review backups first, then acknowledge the new runtime
|
||||||
|
collation version inside the running container:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it geointel psql -U "${GEOINTEL_POSTGRES_USER:-geointel}" -d "${GEOINTEL_POSTGRES_DB:-geointel}"
|
||||||
|
ALTER DATABASE "geointel" REFRESH COLLATION VERSION;
|
||||||
|
```
|
||||||
|
|
||||||
|
If you rely on text indexes with locale-specific ordering, plan a maintenance
|
||||||
|
window and rebuild the affected indexes before acknowledging the version. The
|
||||||
|
current GeoIntel V1 spatial workflows primarily use UUIDs, JSON metadata and
|
||||||
|
PostGIS geometry indexes, but the warning should still be tracked explicitly.
|
||||||
|
|
||||||
## Change the browser port
|
## Change the browser port
|
||||||
|
|
||||||
Edit `.env`:
|
Edit `.env`:
|
||||||
|
|||||||
@@ -1627,3 +1627,30 @@ Limitations:
|
|||||||
|
|
||||||
Next recommended pass:
|
Next recommended pass:
|
||||||
- Run release readiness, rebuild/deploy Tower and verify browser/GIS/demo smoke again.
|
- Run release readiness, rebuild/deploy Tower and verify browser/GIS/demo smoke again.
|
||||||
|
|
||||||
|
## Sprint 36 PostgreSQL collation maintenance visibility (2026-06-17)
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- Added PostgreSQL database collation version reporting to `scripts/live_migration_smoke.sh`.
|
||||||
|
- The live smoke now prints `COLLATION_VERSION_MISMATCH` with stored and actual versions when a reused PostGIS volume was created under an older libc/collation runtime.
|
||||||
|
- The smoke also prints the exact `ALTER DATABASE "... " REFRESH COLLATION VERSION;` acknowledgement command, but does not run it automatically.
|
||||||
|
- Documented the Unraid maintenance procedure and backup/index review guidance.
|
||||||
|
- Added regression coverage for the collation reporting path.
|
||||||
|
|
||||||
|
Tested:
|
||||||
|
- `cd backend && python -m pytest tests/test_live_migration_smoke_script.py -q`
|
||||||
|
- `bash -n scripts/live_migration_smoke.sh`
|
||||||
|
- `python -m compileall backend/app`
|
||||||
|
- `cd backend && python -m pytest -W error::DeprecationWarning`
|
||||||
|
- `cd frontend && npm run typecheck`
|
||||||
|
- `cd frontend && npm run build`
|
||||||
|
- `bash scripts/run_readiness_check.sh`
|
||||||
|
|
||||||
|
Open:
|
||||||
|
- Run the updated smoke against Tower to confirm whether the reused volume still reports a collation mismatch.
|
||||||
|
|
||||||
|
Limitations:
|
||||||
|
- The smoke reports and documents the maintenance action. It intentionally does not mutate collation metadata automatically.
|
||||||
|
|
||||||
|
Next recommended pass:
|
||||||
|
- Run release readiness, deploy Tower and decide whether to manually acknowledge the current collation version.
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ This file now starts with the current implementation status. Older preparation/b
|
|||||||
- [x] Add Unraid Compose template assets with editable ports, storage path and app icon.
|
- [x] Add Unraid Compose template assets with editable ports, storage path and app icon.
|
||||||
- [x] Add single-container Unraid runtime with embedded PostGIS, backend and frontend.
|
- [x] Add single-container Unraid runtime with embedded PostGIS, backend and frontend.
|
||||||
- [x] Remove embedded PostGIS password defaults from all-in-one Docker image metadata.
|
- [x] Remove embedded PostGIS password defaults from all-in-one Docker image metadata.
|
||||||
|
- [x] Report reused-volume PostgreSQL collation mismatches in live migration smoke.
|
||||||
|
|
||||||
## Current implementation status
|
## Current implementation status
|
||||||
|
|
||||||
|
|||||||
@@ -69,6 +69,26 @@ required_objects = [
|
|||||||
with get_engine().connect() as connection:
|
with get_engine().connect() as connection:
|
||||||
postgis_version = connection.execute(text("SELECT PostGIS_Version()")).scalar()
|
postgis_version = connection.execute(text("SELECT PostGIS_Version()")).scalar()
|
||||||
print(f"PostGIS version: {postgis_version}")
|
print(f"PostGIS version: {postgis_version}")
|
||||||
|
collation_row = connection.execute(
|
||||||
|
text(
|
||||||
|
"SELECT datname, datcollversion, pg_database_collation_actual_version(oid) AS actual_version "
|
||||||
|
"FROM pg_database WHERE datname = current_database()"
|
||||||
|
)
|
||||||
|
).mappings().one()
|
||||||
|
stored_version = collation_row["datcollversion"]
|
||||||
|
actual_version = collation_row["actual_version"]
|
||||||
|
if stored_version != actual_version:
|
||||||
|
database_name = str(collation_row["datname"]).replace('"', '""')
|
||||||
|
print(
|
||||||
|
"COLLATION_VERSION_MISMATCH: "
|
||||||
|
f"database={collation_row['datname']} stored={stored_version} actual={actual_version}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"After reviewing backups and indexes, acknowledge with: "
|
||||||
|
f'ALTER DATABASE "{database_name}" REFRESH COLLATION VERSION;'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("Database collation version: ok")
|
||||||
|
|
||||||
missing = [
|
missing = [
|
||||||
object_name
|
object_name
|
||||||
@@ -154,6 +174,26 @@ required_objects = [
|
|||||||
with get_engine().connect() as connection:
|
with get_engine().connect() as connection:
|
||||||
postgis_version = connection.execute(text("SELECT PostGIS_Version()")).scalar()
|
postgis_version = connection.execute(text("SELECT PostGIS_Version()")).scalar()
|
||||||
print(f"PostGIS version: {postgis_version}")
|
print(f"PostGIS version: {postgis_version}")
|
||||||
|
collation_row = connection.execute(
|
||||||
|
text(
|
||||||
|
"SELECT datname, datcollversion, pg_database_collation_actual_version(oid) AS actual_version "
|
||||||
|
"FROM pg_database WHERE datname = current_database()"
|
||||||
|
)
|
||||||
|
).mappings().one()
|
||||||
|
stored_version = collation_row["datcollversion"]
|
||||||
|
actual_version = collation_row["actual_version"]
|
||||||
|
if stored_version != actual_version:
|
||||||
|
database_name = str(collation_row["datname"]).replace('"', '""')
|
||||||
|
print(
|
||||||
|
"COLLATION_VERSION_MISMATCH: "
|
||||||
|
f"database={collation_row['datname']} stored={stored_version} actual={actual_version}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"After reviewing backups and indexes, acknowledge with: "
|
||||||
|
f'ALTER DATABASE "{database_name}" REFRESH COLLATION VERSION;'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("Database collation version: ok")
|
||||||
|
|
||||||
missing = [
|
missing = [
|
||||||
object_name
|
object_name
|
||||||
@@ -267,6 +307,26 @@ required_objects = [
|
|||||||
with get_engine().connect() as connection:
|
with get_engine().connect() as connection:
|
||||||
postgis_version = connection.execute(text("SELECT PostGIS_Version()")).scalar()
|
postgis_version = connection.execute(text("SELECT PostGIS_Version()")).scalar()
|
||||||
print(f"PostGIS version: {postgis_version}")
|
print(f"PostGIS version: {postgis_version}")
|
||||||
|
collation_row = connection.execute(
|
||||||
|
text(
|
||||||
|
"SELECT datname, datcollversion, pg_database_collation_actual_version(oid) AS actual_version "
|
||||||
|
"FROM pg_database WHERE datname = current_database()"
|
||||||
|
)
|
||||||
|
).mappings().one()
|
||||||
|
stored_version = collation_row["datcollversion"]
|
||||||
|
actual_version = collation_row["actual_version"]
|
||||||
|
if stored_version != actual_version:
|
||||||
|
database_name = str(collation_row["datname"]).replace('"', '""')
|
||||||
|
print(
|
||||||
|
"COLLATION_VERSION_MISMATCH: "
|
||||||
|
f"database={collation_row['datname']} stored={stored_version} actual={actual_version}"
|
||||||
|
)
|
||||||
|
print(
|
||||||
|
"After reviewing backups and indexes, acknowledge with: "
|
||||||
|
f'ALTER DATABASE "{database_name}" REFRESH COLLATION VERSION;'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print("Database collation version: ok")
|
||||||
|
|
||||||
missing = [
|
missing = [
|
||||||
object_name
|
object_name
|
||||||
|
|||||||
Reference in New Issue
Block a user