diff --git a/main.py b/main.py index 6cfc428..abc4ab7 100644 --- a/main.py +++ b/main.py @@ -9,6 +9,7 @@ import io import math import datetime import configparser +import threading import urllib.request import urllib.parse import uuid @@ -219,20 +220,40 @@ SETTINGS_SPREADSHEET_ID = '1k7SNEdIaRtGXzQNdJSQAiNSDRwzOePDRTLHY_mNCO8M' MANUAL_HISTORY_FILE = 'manual_history.dat' # ----- Utility Functions ----- -def read_config(): +def read_config(force_refresh=False): + global _CONFIG_ROWS_CACHE, _CONFIG_ROWS_CACHE_TIME + + rows = [] + + # 1. 캐시 유효성 판단 (force_refresh가 아니고, 캐시가 존재하며, TTL이 지나지 않은 경우) + if not force_refresh and _CONFIG_ROWS_CACHE is not None: + if time.time() - _CONFIG_ROWS_CACHE_TIME < CONFIG_CACHE_TTL: + rows = _CONFIG_ROWS_CACHE + + # 2. 캐시가 없거나 만료된 경우 구글 시트에서 직접 새로 로드 + if not rows: + with _CONFIG_LOCK: + # 락 대기 중에 다른 스레드에서 캐시를 갱신했을 수 있으므로 다시 검사 (Double-Checked Locking) + if not force_refresh and _CONFIG_ROWS_CACHE is not None and time.time() - _CONFIG_ROWS_CACHE_TIME < CONFIG_CACHE_TTL: + rows = _CONFIG_ROWS_CACHE + else: + for _ in range(3): + try: + ws = get_settings_worksheet() + rows = ws.get_all_values() + break + except Exception as e: + time.sleep(1.5) + print(f"Google Sheets 불러오기 재시도 중...: {e}") + + if rows: + _CONFIG_ROWS_CACHE = rows + _CONFIG_ROWS_CACHE_TIME = time.time() + + # 3. 로드된 로우 데이터를 ConfigParser 형식으로 파싱 config = configparser.ConfigParser(allow_no_value=True) config.optionxform = str - rows = [] - for _ in range(3): - try: - ws = get_settings_worksheet() - rows = ws.get_all_values() - break - except Exception as e: - time.sleep(1.5) - print(f"Google Sheets 불러오기 재시도 중...: {e}") - if rows and len(rows) > 1: for row in rows: if not row or not row[0].strip(): @@ -256,10 +277,10 @@ def read_config(): config.set(section, key, val) else: config.set(section, key) - config.set(section, key) return config def write_config(config): + global _CONFIG_ROWS_CACHE, _CONFIG_ROWS_CACHE_TIME rows = [["대분류 (Section)", "항목 (Key)", "설정값 1", "설정값 2", "설정값 3", "설정값 4", "설정값 5"]] for section in config.sections(): items = config.items(section) @@ -278,6 +299,11 @@ def write_config(config): ws = get_settings_worksheet() ws.clear() ws.update(values=rows) + + # 구글 시트에 저장이 성공하면 로컬 캐시도 즉시 최신화 + with _CONFIG_LOCK: + _CONFIG_ROWS_CACHE = rows + _CONFIG_ROWS_CACHE_TIME = time.time() return except Exception as e: print(f"Google Sheets 저장 시도 {attempt+1}/3 실패: {e}") @@ -318,6 +344,16 @@ async def get_config(): return JSONResponse(data) +@app.post("/api/config/refresh") +async def refresh_config(): + """구글 시트로부터 실시간으로 설정을 동기화하여 캐시를 강제 갱신합니다.""" + try: + read_config(force_refresh=True) + return {"status": "success", "message": "설정 캐시가 성공적으로 갱신되었습니다."} + except Exception as e: + raise HTTPException(status_code=500, detail=f"설정 캐시 갱신 실패: {str(e)}") + + class ProductGroup(BaseModel): old_code: str @@ -416,7 +452,12 @@ def format_phone_number(num_str): -# ----- Google Sheets Global Cache ----- +# ----- Google Sheets Config Cache & Global Sheets Cache ----- +_CONFIG_ROWS_CACHE = None +_CONFIG_ROWS_CACHE_TIME = 0.0 +_CONFIG_LOCK = threading.Lock() +CONFIG_CACHE_TTL = 600.0 # 캐시 유효 시간: 10분 + gs_client_global = None worksheet_manual_global = None worksheet_settings_global = None @@ -492,6 +533,11 @@ async def startup_event(): get_sms_history_worksheet() get_manual_history_worksheet() print("구글 시트 연동 완료!") + + # 캐시 초기 적재 (Warm up cache) + print("구글 시트 설정 캐시 초기 적재 시작...") + read_config(force_refresh=True) + print("구글 시트 설정 캐시 적재 완료!") except Exception as e: print(f"구글 시트 연동 초기화 실패 (사용 전 권한을 확인하세요): {str(e)}")