Django 서버에서 스케쥴링을 할 일이 있어서 SQLAlchemy 라이브러리를 통해 사용중인 MySQL DB에 job을 저장해서 scheduler를 사용하고있었다.
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from django.conf import settings
class SchedulerManager:
_instance = None
@classmethod
def get_scheduler(cls):
if cls._instance is None:
db_settings = settings.DATABASES['default']
db_url = f"mysql+pymysql://{db_settings['USER']}:{db_settings['PASSWORD']}@{db_settings['HOST']}:{db_settings['PORT']}/{db_settings['NAME']}"
jobstores = {
'default': SQLAlchemyJobStore(url=db_url)
}
cls._instance = BackgroundScheduler(jobstores=jobstores, timezone="Asia/Seoul")
cls._instance.start()
return cls._instance
\
scheduler.py 파일을 만들어서 이런식으로 Django setting파일에서 설정해둔 DB 정보를 가져와서 jobstores설정을 해줬는데, jobstores설정을 안해줬을때 서버를 끄거나 재시동하면 기존에 스케쥴링 해둔 task & job들이 모두 없어지는거에 비해서 설정을 해두면 DB에 저장을 해둬서 재시동해도 재시간이되면 동작을 하고, DB에 접속해서 쿼리문으로 앞으로 실행될 예정인 작업들도 볼수있어서 좋았다.
그런데 분명 테스트를 할때는 잘 실행되던 작업들이 밤만 되면 오류가 나서 실행이 안되는 문제가 발생했다.
처음에는 작업을 스케쥴링 해둔 함수들에 변수로 들어가는 인스턴스가 문제를 일으키는줄 알았는데, 로그 파일에 오류를 확인해보니,
sqlalchemy.exc.OperationalError: (pymysql.err.OperationalError) (2006, "MySQL server has gone away (TimeoutError(110, 'Connection timed out'))")
이런 에러가 떠있었다.
분명 MySQL에 잘 연결돼서 쿼리문으로 예정된 작업까지 확인했는데 이게 무슨 오류지..? 하고 생각했었는데 알고보니 MySQL 서버가 긴 시간동안 사용되지 않아서 연결이 끊어진것이었다.
이 경우에는 두가지를 의심해볼수있는데,
첫번째는 MySQL 설정의 my.cnf 파일에서 wait_timeout과 interactive_timeout 이 몇초로 설정되어있는지를 확인해야한다.
[mysqld]
wait_timeout = 28800
interactive_timeout = 28800
두번째는 Scheduler의 SQLAlchemy 설정에서 pool_recycle, pool_pre_ping을 수정하는것이다.
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.sqlalchemy import SQLAlchemyJobStore
from django.conf import settings
class SchedulerManager:
_instance = None
@classmethod
def get_scheduler(cls):
if cls._instance is None:
db_settings = settings.DATABASES['default']
db_url = (
f"mysql+pymysql://{db_settings['USER']}:{db_settings['PASSWORD']}"
f"@{db_settings['HOST']}:{db_settings['PORT']}/{db_settings['NAME']}"
"?charset=utf8mb4"
)
jobstores = {
'default': SQLAlchemyJobStore(
url=db_url,
engine_options={
'pool_size': 20, # 기본적으로 유지할 DB 갯수
'max_overflow': 10, # pool_size를 넘어서는 요청이 왔을때 추가로 허용하는 최대 연결 갯수
'pool_timeout': 60, # 사용 가능한 연결이 없을때 최대 60초 대기. 이 시간 지나면 Timeout에러
'pool_recycle': 3600, # 오래된 연결을 재설정하는 시간(초). 1시간마다 재연결
'pool_pre_ping': True, # 쿼리 실행전 연결이 유효한지 검사
}
)
}
cls._instance = BackgroundScheduler(
jobstores=jobstores,
timezone="Asia/Seoul",
job_defaults={
'coalesce': True, # Job이 여러 번 스킵된 경우, 마지막 한 번만 실행할지 여부
'max_instances': 3, # 동시에 실행할수있는 Job 갯수
'misfire_grace_time': 60 # Job이 실행시간보다 늦게 실행될때, 허용할 최대 지연시간. 60초 지나면 실행 안하고 건너뜀.
}
)
cls._instance.start()
이렇게 여러 설정들을 추가해주면 오류없이 더 쾌적하게 Django에서 스케쥴러를 사용할수있다.
끗
'Django' 카테고리의 다른 글
Django collectstatic S3 파일 업로드 안되는 오류 (Django static파일 S3 연결하기) (0) | 2024.12.30 |
---|---|
Zappa로 AWS Lambda를 사용해서 Django 서버리스 배포하기 (0) | 2024.12.30 |
TDD (Test-Driven Development) (0) | 2023.11.16 |
Django 프로젝트(1) - Hot🔥Deal Blog (0) | 2023.11.08 |
Django와 SQLite3 (0) | 2023.10.23 |