fix: satellite-download chunk-basiert (max 500 Futures im RAM), restart: on-failure
This commit is contained in:
parent
62494438ef
commit
aaf499352b
5 changed files with 43 additions and 33 deletions
|
|
@ -32,7 +32,7 @@ services:
|
||||||
- SAT_THREADS=16
|
- SAT_THREADS=16
|
||||||
- PYTHONUNBUFFERED=1
|
- PYTHONUNBUFFERED=1
|
||||||
command: ["python3", "-u", "/scripts/download-satellite.py", "/data/satellite.mbtiles"]
|
command: ["python3", "-u", "/scripts/download-satellite.py", "/data/satellite.mbtiles"]
|
||||||
restart: "no"
|
restart: on-failure
|
||||||
deploy:
|
deploy:
|
||||||
resources:
|
resources:
|
||||||
limits:
|
limits:
|
||||||
|
|
|
||||||
|
|
@ -100,8 +100,8 @@ Sources-Cache unter `./build/sources/` – bei Rebuild nicht löschen (spart Re-
|
||||||
Der Download läuft in einem eigenen Container – vollständig unabhängig von
|
Der Download läuft in einem eigenen Container – vollständig unabhängig von
|
||||||
SSH-Sessions und VS Code. Resume-fähig: bei Unterbrechung einfach neu starten.
|
SSH-Sessions und VS Code. Resume-fähig: bei Unterbrechung einfach neu starten.
|
||||||
|
|
||||||
**Ressourcen:** 16 Threads, 1 GB RAM-Limit (IO-bound), 4 CPUs.
|
**Ressourcen:** 16 Threads, 1 GB RAM-Limit (effektiv ~40 MB, chunk-basiertes Processing), 4 CPUs.
|
||||||
**Robustheit:** Download schreibt in `satellite.mbtiles.part` → Martin ignoriert diese Datei während des Downloads. Nach Abschluss automatische Umbenennung zu `satellite.mbtiles`.
|
**Robustheit:** Download schreibt in `satellite.mbtiles.part` → Martin ignoriert diese Datei während des Downloads. Nach Abschluss automatische Umbenennung zu `satellite.mbtiles`. Bei OOM startet der Container automatisch neu (`restart: on-failure`).
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /mnt/services-data/rd13_tile_server
|
cd /mnt/services-data/rd13_tile_server
|
||||||
|
|
|
||||||
|
|
@ -92,14 +92,21 @@ Fix 3: YAML-Duplikat `reservations` in docker-compose.download.yml bereinigt.
|
||||||
|
|
||||||
Martin gestoppt bis Download fertig.
|
Martin gestoppt bis Download fertig.
|
||||||
|
|
||||||
### Prompt 15
|
### Prompt 16
|
||||||
Sicher, es macht nichts wenn wir ihn einfach auslassen bis wir fertig sind.
|
Was machen wir wenn wieder das mem limit vom download gerissen wird?
|
||||||
|
|
||||||
### Antwort 15
|
### Antwort 16
|
||||||
Martin bleibt gestoppt. Download läuft mit .part-Datei, 1GB Limit, 16 Threads.
|
Eigentlicher Bug identifiziert: `futs = [submit_tile(...) for ...]` legte trotz Semaphore alle 5,5 Mio Future-Objekte gleichzeitig in eine Python-Liste → ~500 MB allein für die Liste.
|
||||||
Status:
|
|
||||||
- `satellite.mbtiles.part` wächst (neuer aktiver Download)
|
Fix: Chunk-basiertes Processing mit Generator:
|
||||||
- `satellite.mbtiles` (3.5 GB, alter Resume-Stand) bleibt als Backup
|
- `tile_coords()` Generator – liefert (z,x,y) lazy ohne alles im RAM
|
||||||
|
- Jeweils 500 Coords zu einem Chunk zusammenfassen
|
||||||
|
- Nur 500 Futures gleichzeitig im Speicher statt 5,5 Mio
|
||||||
|
- Ergebnis: ~37 MB RAM statt 500+ MB
|
||||||
|
|
||||||
|
Zusätzlich: `restart: on-failure` in docker-compose.download.yml – bei OOM automatischer Neustart mit Resume.
|
||||||
|
|
||||||
|
Verifiziert: `CPU: 10% RAM: 36.92MiB / 1GiB`, Fortschritts-Logs erscheinen korrekt.
|
||||||
|
|
||||||
|
|
||||||
### Antwort 10
|
### Antwort 10
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# PROJECT_CONTEXT – rd13_tile_server
|
# PROJECT_CONTEXT – rd13_tile_server
|
||||||
|
|
||||||
**Letzte Aktualisierung:** 2026-06-13 – Martin gestoppt (wartend). Satellit-Download läuft in `.part`-Datei (3.5 GB bereits heruntergeladen, resume aktiv). Fixes: .part-Pattern, OOM-Limit 1GB, YAML-Duplikat.
|
**Letzte Aktualisierung:** 2026-06-13 – Satellit-Download läuft stabil: chunk-basiert (max 500 Futures, ~37MB RAM), restart:on-failure, .part-Datei. Martin gestoppt bis Fertigstellung.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,14 @@ def log(msg):
|
||||||
print(msg, flush=True)
|
print(msg, flush=True)
|
||||||
|
|
||||||
|
|
||||||
|
def tile_coords():
|
||||||
|
"""Generator: liefert alle (z,x,y)-Koordinaten ohne sie alle im RAM zu halten."""
|
||||||
|
for z in range(MAX_ZOOM + 1):
|
||||||
|
for x in range(1 << z):
|
||||||
|
for y in range(1 << z):
|
||||||
|
yield z, x, y
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
total = sum(4 ** z for z in range(MAX_ZOOM + 1))
|
total = sum(4 ** z for z in range(MAX_ZOOM + 1))
|
||||||
log("[Sat] Output: %s" % OUTPUT)
|
log("[Sat] Output: %s" % OUTPUT)
|
||||||
|
|
@ -89,31 +97,26 @@ def main():
|
||||||
conn = init_db(OUTPUT)
|
conn = init_db(OUTPUT)
|
||||||
t0 = time.time()
|
t0 = time.time()
|
||||||
n = 0
|
n = 0
|
||||||
# Bounded queue: max THREADS*4 Futures gleichzeitig im Speicher
|
CHUNK = 500 # max Futures gleichzeitig im Speicher (nicht 5.5 Mio)
|
||||||
# verhindert OOM bei 5.5 Mio Tasks
|
|
||||||
sem = threading.Semaphore(THREADS * 4)
|
|
||||||
|
|
||||||
def submit_tile(ex, z, x, y):
|
coords = tile_coords()
|
||||||
sem.acquire()
|
|
||||||
fut = ex.submit(do_tile, conn, z, x, y)
|
|
||||||
fut.add_done_callback(lambda _: sem.release())
|
|
||||||
return fut
|
|
||||||
|
|
||||||
with ThreadPoolExecutor(max_workers=THREADS) as ex:
|
with ThreadPoolExecutor(max_workers=THREADS) as ex:
|
||||||
futs = []
|
while True:
|
||||||
for z in range(MAX_ZOOM + 1):
|
chunk = [(z, x, y) for z, x, y in (next(coords, None) for _ in range(CHUNK)) if (z, x, y) != (None, None, None)]
|
||||||
for x in range(1 << z):
|
if not chunk:
|
||||||
for y in range(1 << z):
|
break
|
||||||
futs.append(submit_tile(ex, z, x, y))
|
futs = {ex.submit(do_tile, conn, z, x, y): 1 for z, x, y in chunk}
|
||||||
for n, f in enumerate(as_completed(futs), 1):
|
for f in as_completed(futs):
|
||||||
f.result()
|
f.result()
|
||||||
if n % 1000 == 0 or n == total:
|
n += 1
|
||||||
dt = time.time() - t0 or 0.001
|
if n % 1000 == 0 or n == total:
|
||||||
eta = (total - n) / n * dt / 3600
|
dt = time.time() - t0 or 0.001
|
||||||
log("[Sat] %5.1f%% %d/%d ok=%d skip=%d err=%d %.0ft/s ETA=%.1fh" % (
|
eta = (total - n) / n * dt / 3600
|
||||||
n / total * 100, n, total,
|
log("[Sat] %5.1f%% %d/%d ok=%d skip=%d err=%d %.0ft/s ETA=%.1fh" % (
|
||||||
stats["ok"], stats["skip"], stats["err"],
|
n / total * 100, n, total,
|
||||||
n / dt, eta))
|
stats["ok"], stats["skip"], stats["err"],
|
||||||
|
n / dt, eta))
|
||||||
with _lock:
|
with _lock:
|
||||||
if pending:
|
if pending:
|
||||||
conn.executemany("INSERT OR REPLACE INTO tiles VALUES(?,?,?,?)", pending)
|
conn.executemany("INSERT OR REPLACE INTO tiles VALUES(?,?,?,?)", pending)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue