ShineBridge v1.8.28 — history.py: load_recent Fix + periodisches Cleanup

- load_recent(): Window-Funktion durch pro-Sensor-Indexabfragen ersetzt
  (SELECT ... ORDER BY ts DESC LIMIT N per sensor_id) — nutzt Index optimal,
  kein Full-Table-Scan mehr auf 1M+ Zeilen beim Start
- Periodisches Cleanup: täglich via Daemon-Thread statt nur beim Start —
  DB bleibt dauerhaft auf RETENTION_DAYS beschränkt
- RETENTION_DAYS: 7 → 14 (explizites Maximum per Konfiguration)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
retr0
2026-05-26 13:31:46 +02:00
parent c4047fc804
commit d9f94d3f28
2 changed files with 24 additions and 17 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
name: ShineBridge
version: "1.8.27"
version: "1.8.28"
slug: shinebridge
description: Growatt Wechselrichter lokal in Home Assistant — Modbus TCP via ShineLAN-X, MQTT Discovery, Web UI
url: https://gitea.bitfire.work/retr0/shinebridge
+23 -16
View File
@@ -7,7 +7,7 @@ from typing import Dict, List, Optional, Tuple
log = logging.getLogger(__name__)
DB_PATH = "/data/history.db"
RETENTION_DAYS = 7
RETENTION_DAYS = 14
_lock = threading.Lock()
_conn: sqlite3.Connection | None = None
@@ -59,9 +59,19 @@ def init_db():
""")
c.commit()
cleanup_old()
_start_cleanup_scheduler()
log.info("History DB initialisiert: %s", DB_PATH)
def _start_cleanup_scheduler():
def _loop():
while True:
time.sleep(86400)
cleanup_old()
t = threading.Thread(target=_loop, daemon=True, name="history-cleanup")
t.start()
def period_key(period_type: str, billing_day: int = 1, billing_month: int = 1) -> str:
import datetime
today = datetime.date.today()
@@ -117,21 +127,18 @@ def write_batch(inv_id: str, ts: float, values: Dict[str, float]):
def load_recent(inv_id: str, limit: int = 300) -> Dict[str, List[Tuple[float, float]]]:
"""Letzte `limit` Messpunkte pro Sensor — zum Befüllen der In-Memory-Deque beim Start."""
with _lock:
rows = _get_conn().execute("""
SELECT sensor_id, ts, value
FROM (
SELECT sensor_id, ts, value,
ROW_NUMBER() OVER (PARTITION BY sensor_id ORDER BY ts DESC) AS rn
FROM measurements
WHERE inv_id = ?
)
WHERE rn <= ?
ORDER BY ts ASC
""", (inv_id, limit)).fetchall()
result: Dict[str, List[Tuple[float, float]]] = {}
for sensor_id, ts, value in rows:
result.setdefault(sensor_id, []).append((ts, value))
c = _get_conn()
sensors = [r[0] for r in c.execute(
"SELECT DISTINCT sensor_id FROM measurements WHERE inv_id = ?", (inv_id,)
).fetchall()]
result: Dict[str, List[Tuple[float, float]]] = {}
for sid in sensors:
rows = c.execute(
"SELECT ts, value FROM measurements "
"WHERE inv_id = ? AND sensor_id = ? ORDER BY ts DESC LIMIT ?",
(inv_id, sid, limit),
).fetchall()
result[sid] = [(ts, val) for ts, val in reversed(rows)]
return result