Handle hostname collisions during heartbeats

This commit is contained in:
2025-10-18 00:49:07 -06:00
parent a2a5c11536
commit 183b3c31be

View File

@@ -2,6 +2,7 @@ from __future__ import annotations
import json import json
import time import time
import sqlite3
from typing import Any, Callable, Dict, Optional from typing import Any, Callable, Dict, Optional
from flask import Blueprint, jsonify, request, g from flask import Blueprint, jsonify, request, g
@@ -79,14 +80,42 @@ def register(
conn = db_conn_factory() conn = db_conn_factory()
try: try:
cur = conn.cursor() cur = conn.cursor()
columns = ", ".join(f"{col} = ?" for col in updates.keys())
params = list(updates.values()) def _apply_updates() -> int:
params.append(ctx.guid) if not updates:
cur.execute( return 0
f"UPDATE devices SET {columns} WHERE guid = ?", columns = ", ".join(f"{col} = ?" for col in updates.keys())
params, params = list(updates.values())
) params.append(ctx.guid)
if cur.rowcount == 0: cur.execute(
f"UPDATE devices SET {columns} WHERE guid = ?",
params,
)
return cur.rowcount
try:
rowcount = _apply_updates()
except sqlite3.IntegrityError as exc:
if "devices.hostname" in str(exc) and "UNIQUE" in str(exc).upper():
# Another device already claims this hostname; keep the existing
# canonical hostname assigned during enrollment to avoid breaking
# the unique constraint and continue updating the remaining fields.
if "hostname" in updates:
updates.pop("hostname", None)
try:
rowcount = _apply_updates()
except sqlite3.IntegrityError:
raise
else:
log(
"server",
"heartbeat hostname collision ignored for guid="
f"{ctx.guid}",
)
else:
raise
if rowcount == 0:
log("server", f"heartbeat missing device record guid={ctx.guid}") log("server", f"heartbeat missing device record guid={ctx.guid}")
return jsonify({"error": "device_not_registered"}), 404 return jsonify({"error": "device_not_registered"}), 404
conn.commit() conn.commit()