1️⃣ 발단
Imgenie 서비스에 mongo db를 달아주고 있던 중, 테스트 코드에서 아래와 같은 오류가 발생했다.
이 오류는 아래 함수에서 발생한 것인데, 저장 전의 auth 객체와 와 실제 db에 저장된 found 객체가 다르다는 것이였다.
def test_find_by_refresh_token(self):
user = self.__user()
auth = self.__auth(user, "123")
found = self.auth_repository.find_by_refresh_token(auth.refresh_token)
assert auth == found ## 여기서 발생!
2️⃣ 이유
파이썬 datetime의 마이크로초의 값이 다른것이 문제인데 found객체의 마이크초 일부가 없는 걸 보니 뭔가 mongo db에서 저장되는 형식이 datetime과 다른것 같다.
(693360 vs 693000)
그리고 mongodb 공식문서에서 다음 글을 찾았다.
BSON Date is a 64-bit integer that represents the number of milliseconds since the Unix epoch (Jan 1, 1970).
BSON Date는 Unix epoch 시간부터의 밀리초를 표현하는 64비트의 정수값입니다.
> https://www.mongodb.com/docs/v4.4/reference/bson-types/#date
파이썬의 datetime은 마이크로초까지 표현하기 때문인 것 같다.
밀리초는 10^-3까지 마이크로초는 10^-6까지 표현한다
693360에서 마지막 360이 날라간 이유도 이것 때문인 것으로 결론지었다.
3️⃣ 해결
원래는 datetime.utcnow를 created_at의 디폴트로 쓰고 있었는데
이를 초기에 microsecond 중 뒤 3글자를 round해서 해결했다.
def utcnow():
now = datetime.utcnow()
return now.replace(microsecond=round(now.microsecond, -3))
class AuthDocument(Document):
...
created_at = DateTimeField(default=utcnow)
좀 더 나은 방법도 있다.
믹스인을 쓰는 방식인데, 이게 유지보수를 생각하면 편한것 같다.
"""
date.py
"""
def utcnow():
now = datetime.utcnow()
return now.replace(microsecond=round(now.microsecond, -3))
class CreatedAtMixin:
created_at = DateTimeField(default=utcnow)
meta = {"abstract": True}
"""
auth.py
"""
class AuthDocument(CreatedAtMixin, Document):
user = ReferenceField(UserDocument, required=True, unique=True)
refresh_token = StringField(required=True)
meta = {"indexes": [{"fields": ["created_at"], "expireAfterSeconds": int(config.refresh_token_exp_period.total_seconds())}]}
def to_dto(self) -> Auth:
return Auth(id=str(self.id), user=self.user.to_dto(), refresh_token=self.refresh_token, created_at=self.created_at)
'프로그래밍 > 파이썬' 카테고리의 다른 글
[Async-SQLModel] SQLModel서 비동기 프로그래밍시 발생하는 문제의 대안 (1) | 2024.04.03 |
---|---|
파이썬, if문보다 try~expect이 더 좋다? (0) | 2023.08.14 |