feat: refresh prices every 5min, add API_UPSTREAM to docker-compose
This commit is contained in:
@@ -15,41 +15,45 @@ from app.services.price_fetcher import fetch_all_prices
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def fetch_prices_and_snapshot(database_url: str) -> None:
|
||||
"""
|
||||
Daily job that fetches all prices and writes a net worth snapshot.
|
||||
|
||||
Runs outside a request context, so it creates its own DB engine and session.
|
||||
All exceptions are caught and logged — the job never raises.
|
||||
"""
|
||||
def _make_engine(database_url: str):
|
||||
engine = create_engine(
|
||||
database_url,
|
||||
connect_args={"check_same_thread": False} if "sqlite" in database_url else {},
|
||||
)
|
||||
|
||||
# Enable WAL mode for SQLite to avoid locking conflicts with the main app
|
||||
if "sqlite" in database_url:
|
||||
@event.listens_for(engine, "connect")
|
||||
def set_wal_mode(dbapi_connection, connection_record):
|
||||
cursor = dbapi_connection.cursor()
|
||||
cursor.execute("PRAGMA journal_mode=WAL")
|
||||
cursor.close()
|
||||
return engine
|
||||
|
||||
LocalSession = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
db = LocalSession()
|
||||
|
||||
def fetch_prices_job(database_url: str) -> None:
|
||||
"""Runs every 5 minutes — fetches all prices, no snapshot."""
|
||||
engine = _make_engine(database_url)
|
||||
db = sessionmaker(autocommit=False, autoflush=False, bind=engine)()
|
||||
try:
|
||||
updated_count = fetch_all_prices(db)
|
||||
logger.info("Price refresh: updated %d price(s)", updated_count)
|
||||
except Exception as exc:
|
||||
logger.error("Price refresh: unexpected error: %s", exc)
|
||||
finally:
|
||||
db.close()
|
||||
engine.dispose()
|
||||
|
||||
|
||||
def fetch_prices_and_snapshot(database_url: str) -> None:
|
||||
"""Runs at midnight — fetches prices and writes a daily net worth snapshot."""
|
||||
engine = _make_engine(database_url)
|
||||
db = sessionmaker(autocommit=False, autoflush=False, bind=engine)()
|
||||
try:
|
||||
# Step 1: Fetch all prices
|
||||
updated_count = fetch_all_prices(db)
|
||||
logger.info("Daily job: fetched/updated %d price(s)", updated_count)
|
||||
|
||||
# Step 2: Calculate net worth
|
||||
net_worth_result = calculate_net_worth(db)
|
||||
logger.info(
|
||||
"Daily job: calculated net worth = %.2f EUR", net_worth_result["total_eur"]
|
||||
)
|
||||
logger.info("Daily job: net worth = %.2f EUR", net_worth_result["total_eur"])
|
||||
|
||||
# Step 3: Upsert today's snapshot
|
||||
today = date.today()
|
||||
breakdown = json.dumps(net_worth_result["accounts"])
|
||||
try:
|
||||
@@ -57,23 +61,20 @@ def fetch_prices_and_snapshot(database_url: str) -> None:
|
||||
if existing:
|
||||
existing.total_eur = net_worth_result["total_eur"]
|
||||
existing.breakdown_json = breakdown
|
||||
logger.info("Daily job: updated existing snapshot for %s", today)
|
||||
logger.info("Daily job: updated snapshot for %s", today)
|
||||
else:
|
||||
db.add(
|
||||
NetWorthSnapshot(
|
||||
snapshot_date=today,
|
||||
total_eur=net_worth_result["total_eur"],
|
||||
breakdown_json=breakdown,
|
||||
)
|
||||
)
|
||||
logger.info("Daily job: created new snapshot for %s", today)
|
||||
db.add(NetWorthSnapshot(
|
||||
snapshot_date=today,
|
||||
total_eur=net_worth_result["total_eur"],
|
||||
breakdown_json=breakdown,
|
||||
))
|
||||
logger.info("Daily job: created snapshot for %s", today)
|
||||
db.commit()
|
||||
except Exception as exc:
|
||||
logger.error("Daily job: failed to upsert snapshot for %s: %s", today, exc)
|
||||
logger.error("Daily job: failed to upsert snapshot: %s", exc)
|
||||
db.rollback()
|
||||
|
||||
logger.info("Daily job: completed successfully for %s", today)
|
||||
|
||||
logger.info("Daily job: completed for %s", today)
|
||||
except Exception as exc:
|
||||
logger.error("Daily job: unexpected error: %s", exc)
|
||||
finally:
|
||||
@@ -94,6 +95,17 @@ def create_scheduler(database_url: str) -> BackgroundScheduler:
|
||||
|
||||
scheduler = BackgroundScheduler(jobstores=jobstores)
|
||||
|
||||
# Refresh prices every 5 minutes
|
||||
scheduler.add_job(
|
||||
fetch_prices_job,
|
||||
trigger="interval",
|
||||
minutes=5,
|
||||
id="price_refresh",
|
||||
replace_existing=True,
|
||||
args=[database_url],
|
||||
)
|
||||
|
||||
# Write net worth snapshot once per day at midnight
|
||||
scheduler.add_job(
|
||||
fetch_prices_and_snapshot,
|
||||
trigger="cron",
|
||||
|
||||
@@ -18,6 +18,8 @@ services:
|
||||
build: ./web
|
||||
ports:
|
||||
- "80:80"
|
||||
environment:
|
||||
- API_UPSTREAM=api:8000
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_healthy
|
||||
|
||||
Reference in New Issue
Block a user