Эх сурвалжийг харах

Fix SQLite database lock issues during import

- Increase busy_timeout from 5s to 15s in storage.py
- Add retry logic (3 attempts with exponential backoff) for import operations
- Handles concurrent access during multi-row JSON imports
Lukas Goldschmidt 1 сар өмнө
parent
commit
c694fe6262

+ 24 - 15
src/astro_mcp/dashboard.py

@@ -187,7 +187,7 @@ async def person_import_form(request: Request):
 
 @router.post("/dashboard/persons/import", response_class=HTMLResponse)
 async def person_import_submit(request: Request):
-    import json as json_mod
+    import json as json_mod, asyncio
     try:
         form = await request.form()
         file = form.get("import_file")
@@ -211,20 +211,29 @@ async def person_import_submit(request: Request):
                 if not name or not bdt or lat is None or lon is None:
                     errors.append(f"Row {i+1}: missing required fields")
                     continue
-                await storage.add_person(
-                    name=name, birth_datetime=bdt,
-                    latitude=float(lat), longitude=float(lon),
-                    elevation=float(entry.get("elevation", 0) or 0),
-                    nickname=entry.get("nickname") or None,
-                    birthplace=entry.get("birthplace") or None,
-                    alive=bool(entry.get("alive", True)),
-                    private=bool(entry.get("private", False)),
-                    gender=entry.get("gender") or None,
-                    description=entry.get("description") or None,
-                    notes=entry.get("notes") or None,
-                    tz=entry.get("timezone") or None,
-                    birth_time_known=bool(entry.get("birth_time_known", True)),
-                )
+                # Retry up to 3 times on locked database
+                for attempt in range(3):
+                    try:
+                        await storage.add_person(
+                            name=name, birth_datetime=bdt,
+                            latitude=float(lat), longitude=float(lon),
+                            elevation=float(entry.get("elevation", 0) or 0),
+                            nickname=entry.get("nickname") or None,
+                            birthplace=entry.get("birthplace") or None,
+                            alive=bool(entry.get("alive", True)),
+                            private=bool(entry.get("private", False)),
+                            gender=entry.get("gender") or None,
+                            description=entry.get("description") or None,
+                            notes=entry.get("notes") or None,
+                            tz=entry.get("timezone") or None,
+                            birth_time_known=bool(entry.get("birth_time_known", True)),
+                        )
+                        break
+                    except Exception as inner:
+                        if "locked" in str(inner).lower() and attempt < 2:
+                            await asyncio.sleep(0.5 * (attempt + 1))
+                            continue
+                        raise
                 imported += 1
             except Exception as e:
                 errors.append(f"Row {i+1}: {e}")

+ 1 - 1
src/astro_mcp/storage.py

@@ -27,7 +27,7 @@ def _get_conn() -> sqlite3.Connection:
     """Get a thread-local SQLite connection."""
     conn = sqlite3.connect(str(DB_PATH))
     conn.execute("PRAGMA journal_mode=WAL")
-    conn.execute("PRAGMA busy_timeout=5000")
+    conn.execute("PRAGMA busy_timeout=15000")
     conn.row_factory = sqlite3.Row
     return conn