New Database
This commit is contained in:
+62
-30
@@ -1,16 +1,12 @@
|
||||
"""Flask application factory and SSE event system."""
|
||||
import json
|
||||
import os
|
||||
import queue
|
||||
import secrets
|
||||
import sqlite3
|
||||
import threading
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from backend.time_util import now as tz_now, strptime as tz_strptime
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||
from backend.models import init_db
|
||||
from backend.models import init_db, get_db
|
||||
from backend.routes import register_routes
|
||||
from backend.events import EventBus
|
||||
from backend.weather_mgr import fetch_weather, fetch_hourly
|
||||
@@ -70,33 +66,69 @@ def create_app(db_path: str = "data.db") -> Flask:
|
||||
return app
|
||||
|
||||
|
||||
def _startup_weather(app, db_path: str):
|
||||
try:
|
||||
conn = sqlite3.connect(db_path)
|
||||
row = conn.execute(
|
||||
"SELECT updated_at FROM weather_cache WHERE user_id=0 ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
conn.close()
|
||||
except Exception:
|
||||
row = None
|
||||
# Startup weather fetch lock: only one Gunicorn worker may fetch at a time
|
||||
# (others skip; the cache is then fresh so the regular scheduler job handles it).
|
||||
_WEATHER_STARTUP_LOCK_KEY = 0x5745415448455253 # "WEATHERS"
|
||||
|
||||
should_fetch = True
|
||||
if row and row[0]:
|
||||
|
||||
def _startup_weather(app, db_path: str):
|
||||
# Multi-worker guard: without this, every worker would hit the weather API
|
||||
# concurrently on boot. Whoever wins the advisory lock runs the check/fetch.
|
||||
lock_conn = None
|
||||
got_lock = False
|
||||
try:
|
||||
import psycopg2
|
||||
lock_conn = psycopg2.connect(db_path, connect_timeout=5)
|
||||
cur = lock_conn.cursor()
|
||||
cur.execute("SELECT pg_try_advisory_lock(%s)", (_WEATHER_STARTUP_LOCK_KEY,))
|
||||
got_lock = bool(cur.fetchone()[0])
|
||||
cur.close()
|
||||
except Exception:
|
||||
if lock_conn is not None:
|
||||
try:
|
||||
lock_conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
if not got_lock:
|
||||
try:
|
||||
updated = tz_strptime(row[0], "%Y-%m-%d %H:%M:%S")
|
||||
age = tz_now() - updated
|
||||
if age < timedelta(minutes=30):
|
||||
should_fetch = False
|
||||
app.logger.info(
|
||||
f"Startup: weather cache is {int(age.total_seconds()/60)} min old, skip fetch"
|
||||
)
|
||||
lock_conn.close()
|
||||
except Exception:
|
||||
pass
|
||||
return
|
||||
|
||||
if should_fetch:
|
||||
try:
|
||||
try:
|
||||
fetch_weather()
|
||||
fetch_hourly()
|
||||
app.logger.info("Startup: weather fetched (cache stale or empty)")
|
||||
except Exception as e:
|
||||
app.logger.warning(f"Startup weather fetch failed: {e}")
|
||||
conn = get_db(db_path)
|
||||
row = conn.execute(
|
||||
"SELECT updated_at FROM weather_cache WHERE user_id=0 ORDER BY id DESC LIMIT 1"
|
||||
).fetchone()
|
||||
conn.close()
|
||||
except Exception:
|
||||
row = None
|
||||
|
||||
should_fetch = True
|
||||
if row and row["updated_at"]:
|
||||
try:
|
||||
updated = tz_strptime(row["updated_at"], "%Y-%m-%d %H:%M:%S")
|
||||
age = tz_now() - updated
|
||||
if age < timedelta(minutes=30):
|
||||
should_fetch = False
|
||||
app.logger.info(
|
||||
f"Startup: weather cache is {int(age.total_seconds()/60)} min old, skip fetch"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if should_fetch:
|
||||
try:
|
||||
fetch_weather()
|
||||
fetch_hourly()
|
||||
app.logger.info("Startup: weather fetched (cache stale or empty)")
|
||||
except Exception as e:
|
||||
app.logger.warning(f"Startup weather fetch failed: {e}")
|
||||
finally:
|
||||
try:
|
||||
lock_conn.close() # releases the advisory lock
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user