diff --git a/BUGS.md b/BUGS.md index b21582d..3541dff 100644 --- a/BUGS.md +++ b/BUGS.md @@ -24,6 +24,20 @@ When CLOSED, move the entire entry to Resolved — don't duplicate. fills the cards immediately on load (and every 15 s) via REST. Needs in-car verification. +### BUG-20 — Server trip IDs inflate far beyond the real trip count +- **Status**: `OPEN` 2026-08-14 +- **Phase**: 29 +- **Symptom**: the portal shows trip IDs in the tens of thousands (e.g. 25136, 27091) even + though there are only ~10 real trips (verified: `COUNT=10`, `MAX(id)=27091`). +- **Cause**: `trips.id` is `BIGSERIAL`, and `compute_trips_for_device()` upserts every trip on + every tracking batch via `INSERT … ON CONFLICT (device_id, start_time) DO UPDATE`. PostgreSQL + consumes a sequence value on every INSERT attempt — including the conflict/update path — so + the sequence advances by the trip count on every recompute (which runs after every upload + batch). Cosmetic only: nothing references `trips.id` as a foreign key (`deleted_trip_markers` + keys on `(device_id, start_time)`). +- **Fix**: clamp the sequence to `MAX(id)` after each recompute in `compute_trips_for_device()`, + plus a one-time migration that renumbers existing trips compactly and resets the sequence. + --- ## Resolved diff --git a/PLAN.md b/PLAN.md index e54e46a..4c08c26 100644 --- a/PLAN.md +++ b/PLAN.md @@ -2279,6 +2279,10 @@ logged as `BUG-XX` entries in [BUGS.md](./BUGS.md) (BUG-16 onward) and fixed + v `SET TIME!`). SIM868 CR1220 installed + its RTC set via `AT+CCLK` (was 2004 default); hold across power-off to confirm on next cycle. Optional follow-up: `AT+CCLK` boot-time clock fallback in `app_gps.py`. +- **BUG-20 (server trip IDs inflate) — 🚧 IN PROGRESS**: `trips.id BIGSERIAL` burns sequence + values on every upsert (recompute runs after each upload batch), so IDs run far past the real + trip count (~10 trips, ids 27000+). Fix: clamp the sequence after each recompute + one-time + renumber migration. Open items from the drive — fill in as reported: - (none clock-related — BUG-17/BUG-18 closed 2026-08-14). diff --git a/server/docker-compose.yml b/server/docker-compose.yml index 9b7d106..5a4b152 100644 --- a/server/docker-compose.yml +++ b/server/docker-compose.yml @@ -14,6 +14,7 @@ services: - ./sql/migrate_phase25_5a.sql:/docker-entrypoint-initdb.d/migrate_phase25_5a.sql:ro - ./sql/migrate_phase25_5c.sql:/docker-entrypoint-initdb.d/migrate_phase25_5c.sql:ro - ./sql/migrate_phase26_deleted_trips.sql:/docker-entrypoint-initdb.d/migrate_phase26_deleted_trips.sql:ro + - ./sql/migrate_phase29_trip_ids.sql:/docker-entrypoint-initdb.d/migrate_phase29_trip_ids.sql:ro healthcheck: test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-yarisobd}"] interval: 10s diff --git a/server/modules/db.py b/server/modules/db.py index 30576f7..2850b3f 100644 --- a/server/modules/db.py +++ b/server/modules/db.py @@ -509,6 +509,13 @@ def compute_trips_for_device(device_id): """, {"device_id": device_id, **t}, ) + # BIGSERIAL consumes a sequence value on every INSERT attempt, even when + # the ON CONFLICT (DO UPDATE) path is taken. Clamp the sequence back to the + # real max id so trip IDs don't inflate by trip_count on every recompute. + cur.execute( + "SELECT setval(pg_get_serial_sequence('trips','id'), " + "(SELECT COALESCE(MAX(id), 0) FROM trips))" + ) return len(trips) diff --git a/server/sql/migrate_phase29_trip_ids.sql b/server/sql/migrate_phase29_trip_ids.sql new file mode 100644 index 0000000..e8588b9 --- /dev/null +++ b/server/sql/migrate_phase29_trip_ids.sql @@ -0,0 +1,26 @@ +-- Phase 29 (BUG-20): compact trip IDs and stop BIGSERIAL inflation. +-- trips.id has no foreign-key references (deleted_trip_markers keys on +-- (device_id, start_time)), so renumbering is purely cosmetic. +BEGIN; + +-- Shift all ids up first so the compact ids (1..N) can't collide with any +-- id that still exists during the renumber. +UPDATE trips SET id = id + 1000000; + +-- Renumber compactly in start_time order. +WITH renumbered AS ( + SELECT id, row_number() OVER (ORDER BY start_time, id) AS new_id + FROM trips +) +UPDATE trips t +SET id = r.new_id +FROM renumbered r +WHERE t.id = r.id; + +-- Reset the sequence to the real max id so the next insert gets MAX(id) + 1. +SELECT setval( + pg_get_serial_sequence('trips', 'id'), + (SELECT COALESCE(MAX(id), 0) FROM trips) +); + +COMMIT;