반응형

 보통 스케쥴러 같은 것을 초기화 할 때 app config 등을 이용하게 되는데요. Appconfig의 ready 메서드를 오버라이드 해서 구현합니다. 사실 공식 문서를 읽어보면, init과 관련된 설명이 많은 것으로 보아, 무언가를 초기화 하는 것과 관련이 있다는 것 정도는 눈치채실 수 있습니다.

 

 프로젝트 구조를 보겠습니다.

 

 

 다른 것은 없고, myapp에서 apps.py와 config의 settings.py만 중점적으로 보겠습니다.

 

 

 먼저, INSTALLED_APPS에 myapp을 추가합니다.

 

 

 다음에, MyappConfig라는 클래스에, ready를 오버라이드 하면, 실행되는 과정에서 myapp패키지의, MyappConfig 클래스에 있는, ready 함수가 호출될 겁니다.

 

 

 그런데, 1이 1번 호출될 줄 알았는데, 2번이나 호출되었습니다. 모종의 이유로 2번이나 불렸다는 이야기입니다.

 


 그러면 이것을 어떻게 1번만 호출되게 바꿀 수 있을까요? 그걸 알아보기 위해서, 디버그 모드를 걸어보겠습니다.

 

 

 스택 프레임을 보니, registry.py의 populate가 눈에 보입니다. 그리고, setup이라는 게 있는데, 이를 통해, ready가 명령이 들어왔을 때 setup하는 과정에서 불리는 함수임을 추론할 수 있습니다.

 

 

 populate 함수를 보면, 어플리케이션의 configuration과 model을 로드하는 함수라고 설명이 되어 있어요. 이 메서드를 보면, lock을 쓰는 부분이 있는데요.

 

 

 이 with절은 lock을 걸었기 때문에 thread safe 합니다. 단지, process가 동시에 접근하는 상황에서 safe 하지 않을 뿐입니다.

 

 

 제 MyAppConfig의 ready 함수는 app_config의 ready 함수가 호출되었기 때문에 호출이 된 것인데요. 이 for문 역시, with lock 절 안에 있었기 때문에, 일단은 thread safe 하긴 하겠네요. 일단, 이 문구를 둘 이상의 쓰레드가 실행하지는 않습니다. 이제, 제 어플리케이션의 ready 함수 안에 process id를 출력해 보도록 하겠습니다.

 

 os.getpid로 프로세스의 id를 출력할 수 있어요.

 

 

 보면, 7820, 10940번 프로세스가 각각 제가 선언한 appconfig의 ready를 실행했음을 알 수 있어요. 잠깐. thread safe 하지만, 프로세스가 동시에 들어왔을 때 안전한 상황은 아닙니다. 그러면 file lock이라도 걸어야 할까요?

 


 그런데, django 공식 문서를 보면, 그냥 flag 변수만을 넣으라고 합니다. 뭔가 거창한 처리가 필요할 거 같은데 말입니다. 사실 정말로 프로세스가 중구난방으로 실행되면 file lock 같이 두 프로세스가 동시에 볼 수 있는 무언가를 가지고 락을 걸어버리는 게 필요하기는 합니다. 그런데, 공식 문서에는 생각보다 간단한 해결책이 있어요. pid가 2번 찍히기 직전의 상황까지 trace를 해 보겠습니다.

 

 trace를 하다 보면, autoreload.py가 있는데요. run_with_reloader 함수가 호출된 시점입니다. execute, __init__.py 이 부분이요. 처음에 ready가 호출되었을 때는 375번째 줄에 있었습니다. __init__.py의 execute 함수는 내부에 for loop가 없습니다. goto 같은 라벨도 없고. 고로, 375번째와 413번째 줄이 같이 실행되었다면, 375번째 줄은 413번째 줄보다 먼저 수행된다는 사실을 간파할 수 있습니다.

 

 그리고, 호출되는 함수들을 보아, reloader가 실행될 예정이라는 합리적인 추론 또한 가능합니다.

 

 

 그리고 이 때, 프로세스 id가 하나밖에 없습니다. 아직, 미지의 프로세스가 실행 전이라는 의미이고, app이 찍혔다는 의미는 이미 1번째 프로세스의 ready가 끝났다는 의미입니다. 이로 미루어 보았을 때, 먼저 runserver를 돌렸을 때, 본 프로세스가 app config가 일어난 다음에, 리로더에 대한 옵션을 따로 주지 않았다면, reloader가 실행됩니다. 그리고 reloader가 실행되면, app config를 다시 하게 될 겁니다. 이를 플로우 차트로 그려보면 아래와 같습니다.

 

 

 결국 app config를 한 번만 하게 하고 싶다면, flag 변수를 두면 됩니다. 이 flag는 다수의 프로세스가, 최소한 두 친구가 볼 수 있어야 하는 무언가입니다. os.environ을 이용하면 그리 복잡하지 않게 할 수 있습니다.

 

 

 처음에 MyappConfig에서 환경 변수를 'APP'을 'True'로 설정합니다. 만약에, 프로세스 둘이 os.environ을 공통으로 볼 수 있는 것이라면, 두 번째로 실행되는 프로세스에 대해서는 os.environ이 이미 'APP'을 가지고 있으니, 1이 출력되지 않을 겁니다.

 

 

 실행 결과는 위와 같습니다.

반응형

댓글을 달아 주세요