fix(scheduler): 修復 Gunicorn 4 workers 重複發送排程通知
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
Some checks failed
CD Pipeline / deploy (push) Has been cancelled
根因:APScheduler 在 openclaw_bot_routes.py 透過 record_once 啟動, 但 record_once 只防止同一 process 內重複;Gunicorn --workers 4 有 4 個獨立 worker process,各自啟動一個 scheduler,導致早報/晚報/Excel 每次觸發都送出 4 份。 修復:start_scheduler() 改用 fcntl.LOCK_EX|LOCK_NB 搶佔 /tmp/openclaw_scheduler.lock, 只有搶到鎖的 worker 啟動排程,其餘 3 個 worker 靜默跳過。 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3300,9 +3300,32 @@ def send_daily_excel():
|
||||
sys_log.error(f"[AutoExcel] {e}")
|
||||
|
||||
|
||||
_sched_lock_fh = None # 持有鎖的 file handle,進程退出時自動釋放
|
||||
|
||||
|
||||
def start_scheduler():
|
||||
"""啟動排程(Flask app 啟動後呼叫)"""
|
||||
global _scheduler
|
||||
"""啟動排程(Flask app 啟動後呼叫)
|
||||
使用 fcntl exclusive lock 確保 Gunicorn 多 worker 環境下只有一個 worker 運行排程。
|
||||
"""
|
||||
global _scheduler, _sched_lock_fh
|
||||
import fcntl, atexit
|
||||
|
||||
lock_path = '/tmp/openclaw_scheduler.lock'
|
||||
try:
|
||||
_sched_lock_fh = open(lock_path, 'w')
|
||||
fcntl.flock(_sched_lock_fh.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||
except OSError:
|
||||
sys_log.info("[OpenClawBot] Scheduler lock busy — another worker owns it, skipping")
|
||||
return
|
||||
|
||||
@atexit.register
|
||||
def _release_sched_lock():
|
||||
try:
|
||||
fcntl.flock(_sched_lock_fh.fileno(), fcntl.LOCK_UN)
|
||||
_sched_lock_fh.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
from apscheduler.schedulers.background import BackgroundScheduler
|
||||
from apscheduler.triggers.cron import CronTrigger
|
||||
@@ -3315,7 +3338,7 @@ def start_scheduler():
|
||||
_scheduler.add_job(send_weekly_report, CronTrigger(day_of_week='mon', hour=9, minute=0))
|
||||
_scheduler.add_job(check_anomalies, CronTrigger(hour='9,12,15,18', minute=0))
|
||||
_scheduler.start()
|
||||
sys_log.info("[OpenClawBot] Scheduler started ✓ (competitor/morning/excel/evening/weekly/anomaly)")
|
||||
sys_log.info("[OpenClawBot] Scheduler started ✓ pid=%d (competitor/morning/excel/evening/weekly/anomaly)", os.getpid())
|
||||
except ImportError:
|
||||
sys_log.warning("[OpenClawBot] APScheduler 未安裝 — 排程功能停用")
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user