Rails 8 dev 서버가 부팅 직후 죽을 때 Solid Queue 가드 확인하기
Rails 8 프로젝트에서 bin/rails server가 부팅 직후 종료됐다. 포트를 잡았다는 로그까지 정상으로 찍힌 다음 조용히 죽어서, 처음에는 포트 충돌이나 PID 파일 문제를 의심했다.
로그 끝을 보니 원인은 웹 서버가 아니라 백그라운드 job 쪽에 있었다.
SolidQueue-1.4.0 Error registering Supervisor(fork)
error: "ActiveRecord::StatementInvalid Could not find table 'solid_queue_processes'"
Could not find table 'solid_queue_processes' (ActiveRecord::StatementInvalid)
Detected Solid Queue has gone away, stopping Puma...
- Gracefully shutting down workers...
Puma 안의 Solid Queue가 Puma를 내린다
Rails 8은 단일 서버 배포를 위해 Solid Queue supervisor를 Puma 프로세스 안에서 돌리는 옵션을 기본 생성한다. config/puma.rb의 이 한 줄이 스위치다.
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"]
플러그인은 supervisor를 fork로 띄우고 살아있는지 감시한다. supervisor가 죽으면 job이 조용히 멈춘 채 웹만 돌아가는 상태가 되므로, 이를 막으려고 Puma까지 함께 내린다. 운영에서는 합리적인 설계다. 문제는 supervisor가 애초에 뜰 수 없는 환경에서도 똑같이 동작한다는 점이다.
development에는 queue 테이블이 없다
이 프로젝트의 job 설정은 production에만 있다.
# config/environments/production.rb
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = { database: { writing: :queue } }
development는 Rails 기본 async adapter를 쓰고, database.yml의 development도 단일 SQLite 파일이다. db/queue_schema.rb를 로드한 적이 없으니 solid_queue_* 테이블이 존재하지 않는다. 즉 development에서 Solid Queue는 쓰이지도 않는데 supervisor만 뜨려다 죽고 있었다.
그럼 스위치는 누가 켰나. .env.example이었다.
SOLID_QUEUE_IN_PUMA=true
SOLID_QUEUE_MAX_CONNECTIONS=5
이 파일을 복사해 만든 로컬 .env에 값이 들어가고, dotenv-rails가 부팅 때 ENV에 주입한다. 두 변수 모두 production 전용이고 실제 배포에서는 Kamal의 env.clear가 직접 넣어주므로, 로컬에 있을 이유가 처음부터 없었다.
"false"는 참이다
끄려고 SOLID_QUEUE_IN_PUMA=false를 넣었더니 그대로 죽었다. 가드가 값이 아니라 존재 여부만 보기 때문이다. Ruby에서 거짓인 값은 nil과 false 둘뿐이고 "false"는 길이 5짜리 참인 문자열이다.
빈 문자열도 마찬가지로 참이라 SOLID_QUEUE_IN_PUMA=도 통하지 않는다. dotenv가 .env에서 값을 다시 채우기 때문에 셸에서 env -u로 지우는 것도 소용없었다. 결국 값을 비교하도록 고쳤다.
plugin :solid_queue if ENV["SOLID_QUEUE_IN_PUMA"] == "true"
Kamal이 컨테이너에 넘기는 값이 정확히 무엇인지는 확인하고 넘어가야 한다. deploy.yml에는 YAML 불리언 true로 적혀 있지만 컨테이너가 받는 것은 문자열이다.
$ docker exec "$CONTAINER" sh -c 'printenv SOLID_QUEUE_IN_PUMA | od -c'
0000000 t r u e \n
"true"가 맞으니 production 동작은 그대로다. 이 확인을 건너뛰었다면 development를 고치면서 production의 백그라운드 job을 조용히 꺼버릴 수 있었다.
회귀 테스트에는 세 번째 조건이 필요하다
수정을 고정하는 테스트를 붙였다. 처음 떠올린 조건은 두 개였다. 가드가 == "true" 비교를 포함할 것, 그리고 .env.example이 이 변수를 설정하지 않을 것.
두 조건만으로는 부족하다. 관련 설정을 전부 지워버리는 변경도 통과하기 때문이다. 그러면 production에서 job이 안 돌게 되는데 테스트는 초록색이다. 그래서 세 번째를 넣었다.
test "production still runs Solid Queue inside Puma" do
assert_equal true, deployment_configuration.dig("env", "clear", "SOLID_QUEUE_IN_PUMA"),
"removing this disables background jobs in production"
end
"잘못된 설정이 없다"만 검사하는 테스트는 삭제로 통과시킬 수 있다. 필요한 설정이 남아 있다는 조건을 같이 걸어야 양쪽이 고정된다. 환경별로 갈리는 설정을 다룰 때는 고친 환경뿐 아니라 건드리지 않은 환경도 함께 assert하는 편이 안전하다.
댓글
아직 댓글이 없습니다.
댓글 남기기