[Django] CPU 사용량 증가 원인 파악(FilterSet model)
2024. 2. 16. 08:54ㆍPython/Django
목차
이슈 상황
- FilterSet의 Meta에 model을 추가하게 되면 해당 모델의 FK(Foreign Key)가 fields에 포함되면 CPU사용량이 급격하게 증가하는 상황이 발생
- CPU가 높은 AWS EC2를 사용하게되면 문제가 되지 않지만 docker로 동작하게되면 cpu가 적어 timeout이 발생하거나 속도가 매우 느려짐
FilterSet 검증
1. 문제의 FilterSet
class StudnetFilterSet(FilterSet):
class Meta:
model = Student
fields = [
'id', 'status', 'name',
'parent', # FK
]
CPU 사용정도
- 38478ms = 38.478s
2. Meta의 model 제거 시
class StudnetFilterSet(FilterSet):
id = filters.NumberFilter(help_text='pk')
status = filters.NumberFilter(help_text='상태')
name = filters.CharFilter(help_text='이름')
parent = filters.NumberFilter(help_text='부모')
class Meta:
fields = [
'id', 'status', 'name',
'parent', # FK
]
문제점
- model이 정의가 되어 있지않으면 각 필드에 대해서 전부 재정의 필요
CPU 사용정도
- 971ms = 0.971s
3. Meta의 fields FK filter 재정의 시
방법 1.
class StudnetFilterSet(FilterSet):
parent = filters.NumberFilter(help_text='부모')
class Meta:
model = Student
fields = [
'id', 'status', 'name',
'parent', # FK
]
방법 2.
class StudnetFilterSet(FilterSet):
class Meta:
model = Student
fields = [
'parent', # FK
]
filter_overrides = {
models.ForeignKey: {
'filter_class': django_filters.NumberFilter,
},
}
CPU 사용정도
- 824ms = 0.824s
FilterSet 내부동작 확인
1. Filter 내부엔 DB Field에 따른 filter가 정의 되어 있다.(FILTER_FOR_DBFIELD_DEFAULTS 확인)
FILTER_FOR_DBFIELD_DEFAULTS = {
...
# Forward relationships
models.OneToOneField: {
'filter_class': ModelChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
'to_field_name': f.remote_field.field_name,
'null_label': settings.NULL_CHOICE_LABEL if f.null else None,
}
},
models.ForeignKey: {
'filter_class': ModelChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
'to_field_name': f.remote_field.field_name,
'null_label': settings.NULL_CHOICE_LABEL if f.null else None,
}
},
models.ManyToManyField: {
'filter_class': ModelMultipleChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
}
},
# Reverse relationships
OneToOneRel: {
'filter_class': ModelChoiceFilter,
'extra': lambda f: {
'queryset': remote_queryset(f),
'null_label': settings.NULL_CHOICE_LABEL if f.null else None,
}
},
...
2. remote_queryset(field) 로직파악
- 아래 내용처럼 field에 해당하는 related_model을 가져온다. CPU 사용량이 증가하는 이유로 추정됨.
def remote_queryset(field):
"""
Get the queryset for the other side of a relationship. This works
for both `RelatedField`s and `ForeignObjectRel`s.
"""
model = field.related_model
# Reverse relationships do not have choice limits
if not hasattr(field, 'get_limit_choices_to'):
return model._default_manager.all()
limit_choices_to = field.get_limit_choices_to()
return model._default_manager.complex_filter(limit_choices_to)
결론
- model을 정의해야되는 경우에 FK field의 related_model에 대한 filter를 사용하지 않는다면 filter_overrides를 사용하거나 field를 재정의 해줘야한다.
'Python > Django' 카테고리의 다른 글
[Django] django-model-utils (0) | 2024.10.29 |
---|---|
[Django] PostgreSQL 파티셔닝(django-postgres-extra) (0) | 2024.07.05 |
[Django] 스로틀링(Throttling) - 요청 속도 제어 (0) | 2024.01.12 |
[Django] annotate DateTimeField 지난 달 구하기 (2) | 2024.01.05 |
[Django] Cache(캐시) (1) | 2024.01.03 |