This commit is contained in:
@@ -320,7 +320,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.108"
|
||||
SYSTEM_VERSION = "V10.109"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import math
|
||||
import time
|
||||
import hashlib
|
||||
import pickle
|
||||
import threading
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from flask import Blueprint, request, render_template
|
||||
from sqlalchemy import func, and_, text, bindparam
|
||||
@@ -699,6 +700,10 @@ class FileLock:
|
||||
|
||||
|
||||
_DASHBOARD_STALE_CACHE_MAX_AGE = 86400
|
||||
_DASHBOARD_REFRESH_STATE = {
|
||||
'started_at': 0,
|
||||
'running': False,
|
||||
}
|
||||
|
||||
|
||||
def _new_dashboard_file_lock():
|
||||
@@ -759,6 +764,38 @@ def _load_stale_full_dashboard_cache(now):
|
||||
)
|
||||
|
||||
|
||||
def _load_expired_shared_full_dashboard_cache(now):
|
||||
"""只讀原 shared cache 檔的過期版本;不讀 clear_cache 後搬走的 stale 檔。"""
|
||||
return _load_dashboard_cache_file(
|
||||
now,
|
||||
_DASHBOARD_SHARED_CACHE_FILE,
|
||||
allow_stale=True,
|
||||
label='過期共享',
|
||||
)
|
||||
|
||||
|
||||
def _trigger_dashboard_background_refresh(reason):
|
||||
"""使用過期 shared cache 先回首頁時,背景補一次 fresh cache,避免使用者卡 10s。"""
|
||||
now_ts = time.time()
|
||||
if _DASHBOARD_REFRESH_STATE['running']:
|
||||
return
|
||||
if now_ts - _DASHBOARD_REFRESH_STATE['started_at'] < 60:
|
||||
return
|
||||
|
||||
def _refresh():
|
||||
_DASHBOARD_REFRESH_STATE['running'] = True
|
||||
try:
|
||||
warm_full_dashboard_cache(reason=reason, force_rebuild=True)
|
||||
except Exception as exc:
|
||||
sys_log.warning(f"[Dashboard] [Cache] 背景預熱失敗: {exc}")
|
||||
finally:
|
||||
_DASHBOARD_REFRESH_STATE['running'] = False
|
||||
|
||||
_DASHBOARD_REFRESH_STATE['started_at'] = now_ts
|
||||
thread = threading.Thread(target=_refresh, name='dashboard-cache-refresh', daemon=True)
|
||||
thread.start()
|
||||
|
||||
|
||||
def _write_shared_full_dashboard_cache(full_data):
|
||||
"""原子寫入跨 worker 共享的商品看板深度快取。"""
|
||||
cache_file = str(_DASHBOARD_SHARED_CACHE_FILE)
|
||||
@@ -1031,6 +1068,11 @@ def get_full_dashboard_data(force_rebuild=False):
|
||||
if shared_full_data:
|
||||
return shared_full_data
|
||||
|
||||
expired_shared_data = None if force_rebuild else _load_expired_shared_full_dashboard_cache(now)
|
||||
if expired_shared_data:
|
||||
_trigger_dashboard_background_refresh('expired_shared_cache')
|
||||
return expired_shared_data
|
||||
|
||||
# V-Opt: 使用檔案鎖避免多 gunicorn worker 同時計算
|
||||
dashboard_lock = _new_dashboard_file_lock()
|
||||
lock_acquired = dashboard_lock.acquire(blocking=False)
|
||||
|
||||
Reference in New Issue
Block a user