feat(config): 구글 시트 설정 데이터 인메모리 캐싱 적용 및 동기화 최적화
- 설정 조회 속도 개선을 위한 인메모리 캐싱 도입: read_config(force_refresh=False)로 변경하여 캐시가 존재하고 유효 기간(10분) 내인 경우 구글 시트 API 호출을 건너뛰고 메모리 데이터 즉시 반환 / 다중 스레드 환경을 고려한 threading.Lock 및 Double-Checked Locking 적용으로 동시성 안전 보장 - 실시간 데이터 일관성 유지: write_config(config) 함수 성공 시 새로 작성한 설정 로우(rows) 데이터로 로컬 캐시 즉시 최신화 - 신규 제어 장치 및 Warm up 추가: /api/config/refresh (POST) API 신설 (구글 시트 웹 문서에서 직접 변경 시 강제로 캐시를 갱신할 수 있는 기능 제공) / 서버 시작(startup_event) 시 캐시 초기 적재(Warm up) 처리 추가로 첫 로딩 딜레이 완전 차단
This commit is contained in:
@@ -9,6 +9,7 @@ import io
|
||||
import math
|
||||
import datetime
|
||||
import configparser
|
||||
import threading
|
||||
import urllib.request
|
||||
import urllib.parse
|
||||
import uuid
|
||||
@@ -219,11 +220,23 @@ SETTINGS_SPREADSHEET_ID = '1k7SNEdIaRtGXzQNdJSQAiNSDRwzOePDRTLHY_mNCO8M'
|
||||
MANUAL_HISTORY_FILE = 'manual_history.dat'
|
||||
|
||||
# ----- Utility Functions -----
|
||||
def read_config():
|
||||
config = configparser.ConfigParser(allow_no_value=True)
|
||||
config.optionxform = str
|
||||
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()
|
||||
@@ -233,6 +246,14 @@ def read_config():
|
||||
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
|
||||
|
||||
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)}")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user