1. Intro
이전 포스트에서는 transaction에 대해 간단한 사용방법에 대해 알아보았습니다. 이번 포스트는 db_router를 django에 적용했을 때 유의해야할 점에 대해 알아보려고 합니다. 프로젝트 내에서 두 가지 Database를 이용할 경우가 생겼었는데, 이 경우 transaction atomic 로직이 제대로 동작하지 않는다는 것을 확인하면서, 어떻게 하면 정상적으로 활용할 수 있을지에 대해 고민했습니다. 그 과정에서 transaction이 일어나는 database를 선택해주어야 한다는 간단한 해결법은 의외였습니다.
2. Database routing
Django 에서 데이터베이스 라우팅(Database Routing)은 여러 데이터베이스를 사용하는 애플리케이션에서 특정 쿼리를 어느 데이터베이스로 보내야 할지 결정하는 메커니즘입니다. 기본적으로 Django는 하나의 데이터베이스를 사용하도록 설정되어 있지만, 여러 데이터베이스를 사용하는 경우 DATABASE_ROUTERS 설정을 통해 라우팅 규칙을 정의할 수 있습니다.
3. Transaction atomic with db_router
Example db settings
DATABASE_ROUTERS = ['path.to.MyAppRouter']
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
},
'myapp_db': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'myapp_db.sqlite3',
}
}
Python
복사
만약 위와 같이 DB 세팅이 돼 있다고 가정해보고 View에서 atomic 로직을 작성한다고 가정해봅시다.
PoC
from django.db import transaction, connections
from rest_framework.viewsets import GenericViewSet
from myapp.models import MyModel
class SomeViewSet(mixins.CreateModelMixin, GenericViewSet):
# ...
def create(self, request, *args, **kwargs):
with transaction.atomic(using='myapp_db'):
my_obj = MyModel.objects.using('myapp_db').create(name='Test')
# 'default' 데이터베이스에 대한 트랜잭션
with transaction.atomic(using='default'):
# transaction logic using default
pass
# 'myapp_db' 데이터베이스에 대한 추가 작업
my_obj.name = 'Updated Test'
my_obj.save(using='myapp_db')
Python
복사
해당 예제와 같이 using이라는 파라미터를 통해 특정 데이터베이스를 명시적으로 지정할 수 있습니다. db_router를 사용하면서 해당 파라미터를 지정해주지 않을 경우 의도하지 않은 동작이 발생할 수 있어 유의해야 합니다.
3. Conclusion
이번 포스트를 작성하게 된 계기는, 프로젝트에서 db_router를 설정하고 transaction atomic을 사용할 일이 있어 삽질했던 내용을 바탕으로 작성하게 됐습니다. using이라는 파라미터 역시 database를 지정해주는 파라미터라는 걸 나중에서야 stackoverflow를 통해 알게 됐고, 내부 코드에서는 이러한 comment가 없어 비교적 오랫동안 삽질했었습니다.
이번 건을 계기로 transaction atomic을 더 잘 활용해볼 수 있을 것 같습니다. :)