"""리더보드 DataFrame 전역 캐시 + 파일 기반 시그널. 매 페이지 로드마다 Google Sheets 를 다시 치면 느리고 429(rate limit) 위험이 있으므로, 최초 1회만 fetch 해서 메모리에 캐시한다. Refresh 버튼을 누르면 invalidate_cache() 로 강제 재fetch. 다른 프로세스가 시그널 파일에 최신 타임스탬프를 기록하면 refresh_if_signal_newer() 가 이를 감지해 갱신한다. (원본 레포의 DB 읽기 분기는 이 Space 에서 불필요하므로 제거 — Google Sheets 직접 읽기만 남김) """ import os import threading import time from sheet_manager.sheet_loader.sheet2df import sheet2df _lock = threading.Lock() _cached_df = None _cache_timestamp = 0.0 SIGNAL_FILE = "/tmp/pia_leaderboard_cache_signal" def get_cached_df(): """캐시된 DataFrame 반환. 최초 호출 시 fetch.""" global _cached_df, _cache_timestamp with _lock: if _cached_df is None: _cached_df = sheet2df() _cache_timestamp = time.time() return _cached_df.copy() def invalidate_cache(): """캐시를 즉시 갱신 (Google Sheets 에서 다시 읽기).""" global _cached_df, _cache_timestamp new_df = sheet2df() with _lock: _cached_df = new_df _cache_timestamp = time.time() def refresh_if_signal_newer(): """시그널 파일이 캐시보다 최신이면 갱신 후 반환. 아니면 캐시 그대로.""" try: if os.path.exists(SIGNAL_FILE): with open(SIGNAL_FILE) as f: signal_ts = float(f.read().strip()) if signal_ts > _cache_timestamp: invalidate_cache() except (ValueError, OSError): pass return get_cached_df()