time.monotonic() 은 0부터 시작할 수 있다

Jungkook Park·2022년 9월 15일
0

일정 시간마다 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_valNone 인 경우들이 발생했다.

디버깅 해보니, 부팅되지 얼마 안 된 환경에서는 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()
profile
취미는 개발, 위스키, 전자기기 @elice.io

0개의 댓글