일정 시간마다 refresh 되는 cache 값을 구현하기 위해 이런 코드를 작성했었다.
import time
CACHE_TTL = 3600
_cache_ts = 0
_cache_val = None
def ensure_cache() -> None:
nonlocal _cache_ts
nonlocal _cache_val
if _cache_ts + CACHE_TTL > time.monotinic():
return
_cache_val = 'some heavy calc vaule'
_cache_ts = time.monotinic()
while True:
ensure_cache()
do_job()
개발 환경에서 문제 없이 작동했는데, 프로덕션에 올라가고 나니 일부 환경에서 do_job()
내부에서 _cache_val
이 None
인 경우들이 발생했다.
디버깅 해보니, 부팅되지 얼마 안 된 환경에서는 time.monotonic()
의 값이 매우 작더라. 대략 추측해보았을 때, 부팅 시점으로부터 지나간 seconds 값으로 보였다.
파이썬 문서에 따르면 이 값은 The reference point of the returned value is undefined
이라 되어 있다. 즉, 값 자체의 의미는 맘대로이고, 그냥 계속 증가하는 값이라는 거다. 그래서 대충 epoch
에 뭔가 보정한 값이겠거나 하고, 적당히 큰 값일 것이라고 생각했는데 큰 실수였다. 심지어는 0보다 작은 값일 수도 있을 것 같다.
코드를 좀 더 살펴보면, OS 환경에 따라 부르는 system call 이 다른데, POSIX 환경에서는 주로 부팅 시점으로부터 지나간 seconds 를 사용하는 것 같다 (https://man7.org/linux/man-pages/man2/clock_getres.2.html; CLOCK_MONOTONIC).
결과적으로 처음의 코드는 모든 상황에 대해 대응하기 위해 이렇게 개선하였다.
import time
CACHE_TTL = 3600
_cache_ts = time.monotonic() - CACHE_TTL # changed!
_cache_val = None
def ensure_cache() -> None:
nonlocal _cache_ts
nonlocal _cache_val
if _cache_ts + CACHE_TTL > time.monotinic():
return
_cache_val = 'some heavy calc vaule'
_cache_ts = time.monotinic()
while True:
ensure_cache()
do_job()