diff --git a/tasks/flightradar24/frontend/main.py b/tasks/flightradar24/frontend/main.py
index b4f4d22..514ab9d 100644
--- a/tasks/flightradar24/frontend/main.py
+++ b/tasks/flightradar24/frontend/main.py
@@ -355,7 +355,26 @@ def monitoring_status():
LIMIT 20
"""
)
- return ok({"latest": latest, "history": history})
+ # live unprocessed count
+ unprocessed = query_one(
+ """
+ SELECT
+ (SELECT COALESCE((state_value->>'last_raw_packet_id')::bigint, 0)
+ FROM fr24.processing_state WHERE state_key = 'preprocess_cursor') AS cursor_id,
+ (SELECT MAX(raw_packet_id) FROM fr24.raw_packets) AS max_id,
+ (SELECT COUNT(*) FROM fr24.raw_packets) AS total
+ """
+ )
+ if unprocessed:
+ cursor_id = unprocessed["cursor_id"] or 0
+ max_id = unprocessed["max_id"] or 0
+ total = unprocessed["total"] or 0
+ pending = max(0, max_id - cursor_id)
+ pending_pct = round(pending / total * 100, 1) if total else 0
+ unprocessed_info = {"pending": pending, "pending_pct": pending_pct, "total": total}
+ else:
+ unprocessed_info = None
+ return ok({"latest": latest, "history": history, "unprocessed": unprocessed_info})
except Exception as e:
return err(str(e))
diff --git a/tasks/flightradar24/frontend/static/monitoring.html b/tasks/flightradar24/frontend/static/monitoring.html
index a911768..e8d9f61 100644
--- a/tasks/flightradar24/frontend/static/monitoring.html
+++ b/tasks/flightradar24/frontend/static/monitoring.html
@@ -165,6 +165,11 @@
—
пакетов / 5 мин
+
+
Обработано
+
—
+
пакетов
+
История (последние 20)
@@ -209,13 +214,20 @@
el.className = 'value ' + (colorClass || 'neutral');
}
- function renderLatest(d) {
+ function renderLatest(d, unprocessed) {
setMetric('m-disk', d.disk_pct, diskColor(d.disk_pct));
const [dbVal, dbUnit] = fmtDb(d.db_size_mb);
setMetric('m-db', dbVal);
document.getElementById('m-db-unit').textContent = dbUnit;
setMetric('m-lag', d.capture_lag_sec, lagColor(d.capture_lag_sec));
setMetric('m-tput', d.throughput_5min.toLocaleString());
+ if (unprocessed) {
+ const pct = 100 - unprocessed.pending_pct;
+ const color = pct >= 95 ? 'green' : pct >= 80 ? 'yellow' : 'red';
+ setMetric('m-pending', pct.toFixed(1) + '%', color);
+ document.getElementById('m-pending-unit').textContent =
+ `обработано (${(unprocessed.total - unprocessed.pending).toLocaleString()} из ${unprocessed.total.toLocaleString()})`;
+ }
}
function renderHistory(rows) {
@@ -243,7 +255,7 @@
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const data = await res.json();
banner.style.display = 'none';
- if (data.latest) renderLatest(data.latest);
+ if (data.latest) renderLatest(data.latest, data.unprocessed);
if (data.history) renderHistory(data.history);
document.getElementById('last-updated').textContent =
'Обновлено: ' + new Date().toLocaleTimeString('ru-RU');
diff --git a/tasks/flightradar24/monitoring/main.py b/tasks/flightradar24/monitoring/main.py
index 2f82fbe..1e98f67 100644
--- a/tasks/flightradar24/monitoring/main.py
+++ b/tasks/flightradar24/monitoring/main.py
@@ -87,6 +87,21 @@ def run_checks():
)
throughput = cur.fetchone()[0]
+ # Unprocessed packets (cursor lag)
+ cur.execute(
+ """
+ SELECT
+ (SELECT COALESCE((state_value->>'last_raw_packet_id')::bigint, 0)
+ FROM fr24.processing_state WHERE state_key = 'preprocess_cursor') AS cursor_id,
+ (SELECT MAX(raw_packet_id) FROM fr24.raw_packets) AS max_id,
+ (SELECT COUNT(*) FROM fr24.raw_packets) AS total
+ """
+ )
+ row = cur.fetchone()
+ cursor_id, max_id, total_packets = row if row else (0, 0, 0)
+ unprocessed = max(0, (max_id or 0) - (cursor_id or 0))
+ unprocessed_pct = round(unprocessed / total_packets * 100, 1) if total_packets else 0
+
# Write metrics row
disk_pct_int = int(disk_pct_str) if disk_pct_str not in ("?",) else None
db_size_mb = db_bytes / (1024 ** 2)
@@ -124,7 +139,8 @@ def run_checks():
disk_display = f"{disk_pct_str}%" if disk_pct_str != "?" else "?"
print(
f"[monitor] disk={disk_display} db_size={db_size_str} "
- f"capture_lag={lag_str} throughput={throughput}pkt/5min",
+ f"capture_lag={lag_str} throughput={throughput}pkt/5min "
+ f"unprocessed={unprocessed}({unprocessed_pct}%)",
flush=True,
)