fix(cluster): drop the worked index when a QSO is logged

AddQSO emitted qso:logged but never invalidated the cluster status snapshot.
The frontend did its part - it re-queried every visible spot two seconds later -
and ClusterSpotStatuses answered out of a snapshot built before the contact, so
it returned exactly the same "new band" as before. For ever, until an import, an
edit or a profile switch happened to invalidate it for another reason.

Reported twice: an E51 and then a ZD7 that stayed yellow on the band map with
the QSO plainly in the log. The first report was answered by fixing the
visibility gate, which was a real bug of its own and hid this one.

Confirmed against the operator's MySQL logbook rather than guessed: ZD7BG has
dxcc=250 on all eleven QSOs and 0 of 29579 rows lack a DXCC number, which ruled
out the missing-entity-number theory and pointed here.

Deliberately not invalidateAwardStats(): that also drops the award matrices,
which are expensive on a large log, and a contest run would pay for it once per
QSO. This index is a few DISTINCT scans and it is what the spot colours read.

Also fixes the new worked-grid query, which asked for a column named
"gridsquare". The column is "grid" - gridsquare_ext is a different one - so NEW
GRID could never have worked on either backend. Verified against the real
database: 12516 distinct grid|mode pairs.
This commit is contained in:
2026-08-10 19:18:49 +02:00
parent d6ed9f03eb
commit 0d48dbfd17
3 changed files with 25 additions and 4 deletions
+17
View File
@@ -2672,6 +2672,23 @@ func (a *App) AddQSO(q qso.QSO) (id int64, err error) {
// and the audio was discarded (recordings silently stopped working). The
// snapshot is an in-memory copy; the heavy file encode still runs async.
a.saveQSORecording(&q)
// Drop the cluster worked-index snapshot BEFORE announcing, so the refresh
// the frontend fires on qso:logged rebuilds it and sees this QSO.
//
// Without this the spot colours never moved after a contact. Logging emitted
// the event, the frontend dutifully re-queried every visible spot two
// seconds later, and ClusterSpotStatuses answered out of a snapshot built
// before the QSO — the same "new band" as before, for ever. Reported on an
// E51 and again on a ZD7 that stayed yellow on the band map with the QSO
// plainly in the log.
//
// Deliberately NOT invalidateAwardStats(): that also drops the award
// matrices, which are expensive to rebuild on a large log, and a contest run
// would pay for it once per QSO. This index is a handful of DISTINCT scans
// and it is what the spot colours actually read.
a.clusterStatusMu.Lock()
a.clusterStatusIdx = nil
a.clusterStatusMu.Unlock()
// Announce the log RIGHT AWAY so the grid/UI refresh at once and the entry
// form clears immediately — the operator is not made to wait on the DB.
wruntime.EventsEmit(a.ctx, "qso:logged", id)