Django Model Timemachine 만들기

Jeonghoon·2022년 7월 29일
0

django

목록 보기
2/3

어쩌다 마주하는 상황들

우리는 만든 서비스를 운영하다가 종종 마주하는 상황들이 있다.

그중에서 대량으로 데이터베이스에 잘못된 데이터가 들어간 경우가 존재해 해당하는 부분만 일정 시간으로 롤백해야 하는 경우가 있다.

나는 그러한 경우를 마주했을 때 어떻게 해결했는지 적을려고 한다.

발단

문제의 발생은 이러했다.

새로운 기능으로 대량의 개체 상태를 변경시키는 기능이 개발이 되어졌다.

해당 기능은 query paramater 로 필터를 걸어 대량의 개체를 선택해 임의의 상태로 변경시키는 기능이었다.

뭔가 문제가 생길 것 같은 기능이기도 했지만 요청사항이었기에 만들어졌고 해당하는 기능이 배포가 되어지고 며칠이 지나고 일이 터졌다.

프론트에서 query paramater 를 제대로 보내지 않았고 사용자가 선택하지 않은 개체들도 선택되어서 수만 건의 데이터가 임의의 상태이동이 되어버린 것이다.

파악

가장 먼저 한 것은 몇 건의 데이터가 상태 이동이 되었는지 알아야 했다.

이때 도움이 되었던 것이 pghistory 의 context 였다.

pghistory 는 모델에 대한 이력을 남기는 것의 중점을 두는 패키지이다.

당시 해당하는 패키지를 통해서 어느정도의 이력관리가 되어있었고 또한 pghistory 의 middleware를 커스텀을 해서 어떤 사용자가 어떤 행동을 했는지 추적을 하고 있었기에 빠르게 알 수 있었다.

결국에 얼마만큼의 데이터가 이동이 되었는지 알았는데 대략 8만건의 데이터가 잘못된 상태이동을 한 상태였다.

해결방법

여기에서 해결방법은 두 가지였다.
1. 사건이 일어난 시점을 기준으로 미만인 이벤트 데이터 중 최신 데이터를 가져와서 덮어씌우기를 진행한다.
2. pghistory 의 context를 통해서 해당하는 action 을 기준으로 기준 시간 미만인 데이터 중 최신을 가져온다.

같은 점은 시간으로 해결하지만 다른 점은 시간을 직접 설정하느냐 간접적으로 설정하느냐에 있다.

첫번째 방식은 본인이 해당하는 정확한 시간을 알아야 한다는 점이 있고 또한 표현 방법도 정확해야 한다.

만약에 본인이 해당하는 표현 방식이 틀릴 경우 잘못된 시간을 기입하는 실수가 일어나 또 다시 실수를 반복하게 될 것이다.

하지만 두번째 방식은 위의 방식보다 나은 것이 해당 context 만 알면 context 기반의 시간으로 처리할 수 있기 때문에 code로 깔끔하게 처리가 가능하다.

그래서 나는 context 기반으로 처리를 했다.

코드를 간략하게 핵심만 적으면

item_events = ItemEvent.objects.filter(pgh_created_at__lt=self.validated_data['context'].created_at)
items = Item.objects.filter(id__in=[event.id for event in item_events])
item_events = item_events.filter(id=OuterRef('id'))

annotate_kwargs = {}
update_kwargs = {}

for field in self.validated_data['fields']:
	annotate_kwargs.update({
		f'last_{field}': Subquery(item_events.values(field)[:1])
	})

	update_kwargs.update({field: F(f'last_{field}')})

items.annotate(**annotate_kwargs).update(**update_kwargs)

self.instance = {'items' : items}

이렇게 되어진다.

선행조건은
1. context 기반으로 이력이 관리가 되었는지?
2. 해당하는 액션이 있는 context 가 무엇인지?
3. 되돌려야 하는 fields 는 어떤 것이 있는지?

이다.

결론

일단 이번 일로 아쉬운 점이 있었다.
테스트 코드의 부재가 이렇게 크나큰 일이 생긴다는 것이다.
테스트 코드가 모든 문제를 사전에 파악하지는 못하지만 대부분의 문제를 사전에 파악할 수 있다.
특히 지금과 같이 새로운 기능을 업데이트 하는 경우에는 더더욱 그럴 것 이다.
만약에 귀찮거나 시간이 없다는 핑계로 테스트 코드가 현재처럼 계속 없다면 이번 일은 마지막이 아니고 시작일 것이다.

일단 pghistory 가 아니더라도 이력 관리 패키지 ,db backup, log 관리 등등으로 위의 문제를 해결할 수 있을 것 이다.
그저 그냥 pghistory 가 이력관리를 해주고 특정한 조건을 찾을 수 있었기에 가장 편리한 작업 방식이라고 생각해서 진행을 하게 된 것이다.
요지는 그러하다 데이터를 기록하는 것을 관리하는 것이 필요하다는 것이다.
그래야지 문제가 생겨도 복구가 가능하다.

해당 관련 깃헙주소

profile
개발중

0개의 댓글