geenforce: order backup revisions in Python, not in MySQL

The reports table 500'd on every load: the backup lookup ordered with
ORDER BY lastseenat DESC NULLS LAST, which SQLite accepts and MySQL
rejects outright. Every test passed and the real database refused the
query - the tests run on SQLite, so the dialect difference was invisible.

Sorting in Python removes the dependency for nothing: the rows are one per
host per kind. The regression test pins which revision wins, including
that one never confirmed does not, and says why the sort lives here so it
does not get helpfully moved back into SQL.
This commit is contained in:
cproudlock
2026-08-12 16:49:58 -04:00
parent 52eb10f5ca
commit f1f573862d
2 changed files with 55 additions and 2 deletions

View File

@@ -11,6 +11,7 @@ Two audiences:
import ipaddress
import os
import time
from datetime import datetime
from functools import wraps
from flask import Blueprint, request, Response, send_file, current_app, g
@@ -24,6 +25,9 @@ from shopdb.api import (
SHAREROOT_SETTING = 'geenforce_share_root'
# Sort floor for revisions with no lastseenat (they sort last regardless).
_EPOCH = datetime.min
from ..models import (
ManifestScope, ManifestEntry, ManifestPublishedVersion,
ManifestEnforcementReport, ManifestEnforcementResult, ManifestPayload,
@@ -1037,12 +1041,19 @@ def _attach_backup_state(facts, hostnames):
return
rows = (BackupRevision.query
.filter(BackupRevision.sourcehostname.in_(hostnames))
.order_by(BackupRevision.lastseenat.desc().nullslast())
.all())
# Newest confirmed first, ordered HERE rather than in SQL: putting nulls
# last needs ORDER BY ... NULLS LAST, which SQLite accepts and MySQL
# rejects outright - so the query passed every test and 500'd on the real
# database. Row counts here are one per host per kind, so sorting in Python
# costs nothing.
rows.sort(key=lambda revision: (revision.lastseenat is not None,
revision.lastseenat or _EPOCH),
reverse=True)
for revision in rows:
key = (revision.sourcehostname or '').lower()
entry = facts.get(key)
# First row per host wins: ordered newest-confirmed first.
# First row per host wins.
if entry and entry['backuplastseen'] is None:
entry['backupkind'] = revision.backupkind
entry['backuplastseen'] = (