개발 중 대량의 데이터가 적재된 파일들을 읽어서 찾고자 하는 키워드가 포함된 라인 수를 세는 로직을 개발하게 됐다. 하나의 파일에는 대략 3천만 ~ 5천만 라인의 데이터가 있어 하나의 파일을 읽어드리는데도 약 20초의 시간이 소요됐다. 하지만 이러한 파일들의 수가 많았기 때문에, 기존에 모두 순차적으로 읽어드리는 방식에서 멀티 프로세스를 사용하여 병렬로 처리하도록 로직을 아래와 같이 바꿨다.
try:
files = os.listdir(path_to_search)
except FileNotFoundError:
raise FileNotFoundException({"message": "설정 파일을 찾을 수 없습니다."})
pool = concurrent.futures.ProcessPoolExecutor(max_workers=len(files))
procs = []
for month in range(start_month, end_month + 1):
file_name = 'gcp_tm.' + str(month)
if file_name in files:
procs.append(pool.submit(count_keyword_from_file, file_name, keyword, path_to_search))
total_count = 0
for p in concurrent.futures.as_completed(procs):
total_count += p.result()
별 문제가 없어보이는 로직이지만 아래와 같은 에러가 발생했다. 실행 중이거나 대기 중에 프로세스 pool의 프로세스가 갑자기 종료 됐다고 한다. 처음엔 max_workers의 수 때문에 일 수도 있다는 글을 참고하여 변경해 보았지만 동일한 에러가 발생했다.
concurrent.futures.process.BrokenProcessPool: A process in the process pool was terminated abruptly while the future was running or pending.
해당 글을 보고 문제를 해결했다. 해당 글에선 Django에서 멀티프로세싱 사용 시, 애플리케이션, 모델 및 기타 구성 요소가 모두 로드 되도록 하려면 각 하위 프로세스에서 Django 애플리케이션을 초기화 하라고 한다. django.setup
을 사용하여 pool을 초기화 해야한다고 한다. 아래와 같이 수정하여 문제를 해결할 수 있었다.
concurrent.futures.ProcessPoolExecutor(max_workers=len(files), initializer=django.setup)
Django 프로젝트에서 멀티프로세싱을 사용할 때 각 하위 프로세스에서 Django의 설정과 앱을 올바르게 초기화하는 것은 중요하다. 그렇지 않으면, 모델이나 다른 Django 구성 요소에 대한 참조가 올바르게 해석되지 않아 예기치 않은 동작이나 에러가 발생할 수 있다.
initializer 매개변수와 django.setup() 함수를 사용하여 ProcessPoolExecutor의 각 프로세스가 시작될 때 Django 환경을 초기화하는 것은 이러한 문제를 방지할 수 있다.
django.setup()은 Django의 설정을 구성하고 앱을 로드하는 함수로, 보통 Django 프로젝트의 WSGI 또는 ASGI 애플리케이션에서 호출된다. 앞서 말했 듯, 멀티프로세싱 환경에서 Django 모델이나 기타 구성 요소를 사용하려면 각 하위 프로세스에서도 Django 환경이 올바르게 설정되어 있어야 하기 때문에, initializer 매개변수를 사용하여 ProcessPoolExecutor에게 각 프로세스가 시작될 때 django.setup()을 호출하도록 지시할 수 있다.
이 방법을 사용하면 각 하위 프로세스에서 Django 애플리케이션과 모델이 올바르게 초기화되어, 멀티프로세싱 환경에서도 Django의 기능을 안정적으로 사용할 수 있다.