""" WSGI entry point for Gunicorn production server. Usage: gunicorn -w 8 -b 0.0.0.0:5000 wsgi:app Only one worker starts the scheduler (file-lock based). """ import os import fcntl import time from app import app, init_db, crawl_update_all, is_match_day, db_health_check # Initialize database on first import init_db() # File-lock based singleton scheduler: only one gunicorn worker wins the race LOCK_FILE = "/tmp/short_track_scheduler.lock" scheduler_started = False try: lock_fd = os.open(LOCK_FILE, os.O_CREAT | os.O_WRONLY, 0o644) try: fcntl.flock(lock_fd, fcntl.LOCK_EX | fcntl.LOCK_NB) # We got the lock — start scheduler (only one worker will reach here) from apscheduler.schedulers.background import BackgroundScheduler _last_non_match_day_run = [0] def smart_crawl_update(): """智能爬取:比赛日5分钟一次,非比赛日7天一次""" try: match_day = is_match_day() except Exception: match_day = False if match_day: print("比赛日 - 执行数据更新") crawl_update_all() else: now = time.time() SEVEN_DAYS = 7 * 24 * 3600 if now - _last_non_match_day_run[0] >= SEVEN_DAYS: print("非比赛日 - 7天兜底更新") crawl_update_all() _last_non_match_day_run[0] = now scheduler = BackgroundScheduler() scheduler.add_job( func=smart_crawl_update, trigger="interval", minutes=5, id="auto_update", name="智能自动更新(比赛日5分钟,非比赛日7天)", replace_existing=True, max_instances=1, coalesce=True, misfire_grace_time=120, ) scheduler.add_job( func=db_health_check, trigger="interval", minutes=5, id="db_health_check", name="DB完整性检查+自动恢复", replace_existing=True, max_instances=1, coalesce=True, misfire_grace_time=120, ) scheduler.start() scheduler_started = True print("已启动智能定时更新:比赛日5分钟一次,非比赛日7天一次") print("已启动 DB 完整性检查:每5分钟一次") # 启动时立即执行一次健康检查 db_health_check() except BlockingIOError: # Another worker already holds the lock — skip print("调度器已在其他 worker 运行,跳过") finally: # Don't close lock_fd — we need to hold it for the process lifetime pass if __name__ == "__main__": app.run()