Zeitwerk를 사용한 Docker 개발 환경에서 Sidekiq이 시작되지 않습니다

최근 2.4.0.beta4에서 2.4.0.beta5로 업그레이드한 후 다음 트레이스백과 함께 sidekiq가 시작되지 않습니다:

> $ bundle exec sidekiq -C config/sidekiq.yml
> uninitialized constant SiteSetting::SiteSettingExtension
> /discourse/app/models/site_setting.rb:5:in `<class:SiteSetting>'
> /discourse/app/models/site_setting.rb:3:in `<top (required)>'
> /discourse/vendor/bundle/ruby/2.5.0/gems/zeitwerk-2.1.10/lib/zeitwerk/kernel.rb:23:in `require'
> /discourse/vendor/bundle/ruby/2.5.0/gems/zeitwerk-2.1.10/lib/zeitwerk/kernel.rb:23:in `require'
> /discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-6.0.0/lib/active_support/dependencies/interlock.rb:14:in `block in loading'
> /discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-6.0.0/lib/active_support/concurrency/share_lock.rb:151:in `exclusive'
> /discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-6.0.0/lib/active_support/dependencies/interlock.rb:13:in `loading'
> /discourse/config/initializers/004-message_bus.rb:120:in `<top (required)>'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/engine.rb:667:in `block in load_config_initializer'
> /discourse/vendor/bundle/ruby/2.5.0/gems/activesupport-6.0.0/lib/active_support/notifications.rb:182:in `instrument'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/engine.rb:666:in `load_config_initializer'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/engine.rb:624:in `block (2 levels) in <class:Engine>'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/engine.rb:623:in `each'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/engine.rb:623:in `block in <class:Engine>'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/initializable.rb:32:in `instance_exec'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/initializable.rb:32:in `run'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/initializable.rb:61:in `block in run_initializers'
> /usr/local/lib/ruby/2.5.0/tsort.rb:228:in `block in tsort_each'
> /usr/local/lib/ruby/2.5.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
> /usr/local/lib/ruby/2.5.0/tsort.rb:422:in `block (2 levels) in each_strongly_connected_component_from'
> /usr/local/lib/ruby/2.5.0/tsort.rb:431:in `each_strongly_connected_component_from'
> /usr/local/lib/ruby/2.5.0/tsort.rb:421:in `block in each_strongly_connected_component_from'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/initializable.rb:50:in `each'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/initializable.rb:50:in `tsort_each_child'
> /usr/local/lib/ruby/2.5.0/tsort.rb:415:in `call'
> /usr/local/lib/ruby/2.5.0/tsort.rb:415:in `each_strongly_connected_component_from'
> /usr/local/lib/ruby/2.5.0/tsort.rb:349:in `block in each_strongly_connected_component'
> /usr/local/lib/ruby/2.5.0/tsort.rb:347:in `each'
> /usr/local/lib/ruby/2.5.0/tsort.rb:347:in `call'
> /usr/local/lib/ruby/2.5.0/tsort.rb:347:in `each_strongly_connected_component'
> /usr/local/lib/ruby/2.5.0/tsort.rb:226:in `tsort_each'
> /usr/local/lib/ruby/2.5.0/tsort.rb:205:in `tsort_each'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/initializable.rb:60:in `run_initializers'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/application.rb:363:in `initialize!'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/railtie.rb:190:in `public_send'
> /discourse/vendor/bundle/ruby/2.5.0/gems/railties-6.0.0/lib/rails/railtie.rb:190:in `method_missing'
> /discourse/config/environment.rb:7:in `<top (required)>'
> /discourse/vendor/bundle/ruby/2.5.0/gems/sidekiq-5.2.7/lib/sidekiq/cli.rb:288:in `boot_system'
> /discourse/vendor/bundle/ruby/2.5.0/gems/sidekiq-5.2.7/lib/sidekiq/cli.rb:46:in `run'
> /discourse/vendor/bundle/ruby/2.5.0/gems/sidekiq-5.2.7/bin/sidekiq:12:in `<top (required)>'
> /discourse/vendor/bundle/ruby/2.5.0/bin/sidekiq:23:in `load'
> /discourse/vendor/bundle/ruby/2.5.0/bin/sidekiq:23:in `<top (required)>'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/cli/exec.rb:74:in `load'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/cli/exec.rb:74:in `kernel_load'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/cli/exec.rb:28:in `run'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/cli.rb:463:in `exec'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/vendor/thor/lib/thor/command.rb:27:in `run'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/vendor/thor/lib/thor/invocation.rb:126:in `invoke_command'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/vendor/thor/lib/thor.rb:387:in `dispatch'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/cli.rb:27:in `dispatch'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/vendor/thor/lib/thor/base.rb:466:in `start'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/cli.rb:18:in `start'
> /usr/local/bin/bundle:30:in `block in <main>'
> /usr/local/lib/ruby/site_ruby/2.5.0/bundler/friendly_errors.rb:124:in `with_friendly_errors'
> /usr/local/bin/bundle:22:in `<main>'

다음으로 어디를 확인해야 할지 조언을 주실 수 있나요? 감사합니다!

이것은 공식 가이드를 따르지 않은 설치이므로, 도움을 드릴 수 있는 부분이 많지 않습니다.

현재 OpenShift에서 Discourse를 실행 중이므로 표준 설치 방식은 아닐 것입니다. 공식 가이드를 다시 검토하여 현재 배포 환경에서 놓친 부분이 있는지 확인해 보겠습니다. 감사합니다.

사실 다음 도커 명령어도 VirtualBox의 Discourse Vagrant 머신에서 동일한 스택 트레이스를 발생시키고 있습니다:

vagrant ssh -c '(cd ~/discourse && sudo bin/docker/sidekiq)'

정말 확실합니까? 공식 가이드에는 sidekiq를 수동으로 시작하는 것과 관련된 내용이 없으며, 해당 폴더에 /home/discourse 폴더도 존재하지 않습니다.

프로덕션 환경에서 개발용 설치를 실행하고 계신 건가요 :exploding_head:??

저는 docker 명령어를 사용하여 discourse 플러그인을 테스트하고 개발하고 있습니다.

현재 사용 중인 다른 스크립트는 다음과 같습니다:

# 임시 discourse 캐시 삭제 및 개발용 rails 실행
vagrant ssh -c '(rm -rf ~/discourse/tmp/cache)'
vagrant ssh -c '(cd ~/discourse && sudo bin/docker/rails s)'

물론이죠 :smile:

@Falco 이건 다음과 관련이 있는 것 같습니다.

어떻게 생각하시나요?

아, 그럴 수도 있겠네요. 이것이 개발 환경이 아니라 개발용 설정(dev setup)이라는 점을 명시하지 않으셨기 때문에 상황이 더 복잡해졌습니다.

그래서 지금 말씀하시는 건, 우리의 Docker 개발용 설정이 현재 깨져 있다는 건가요?

네, 그런 것 같습니다.

그 커밋을 되돌리면 사이드킥이 다시 작동합니다.

이건 당신 몫인 것 같네요 @kris.kotlarek

음, 여기서 무슨 일이 있었는지 알겠어요. 제 잘못이에요. Zeitwerk 자동 로더를 사용할 때 더 이상 필요하지 않으므로 require_dependency를 많이 제거했거든요.

하지만 application.rb에는 다음과 같은 코드가 있습니다:

if !Sidekiq.server?
  config.autoload_paths += Dir["#{config.root}/lib"]
end

즉, Sidekiq은 의존성을 찾기 위해 lib 디렉토리를 참조하지 않으며, 특정 파일에서 필요한 내용을 명시적으로 정의하고 있습니다.

Sidekiq에서 사용되는 파일에 대한 require_dependency를 되돌리거나, application.rb의 해당 가드 조건을 제거할 수 있습니다.

이 명시적인 require를 사용하여 워커의 메모리를 절약하려고 했을 것 같으니, 아마 이 방향을 따르는 것이 좋겠습니다. require_dependency를 되돌리겠습니다.

@sam 어떻게 생각하시나요?

가드를 제거해야 합니다. 프로세스가 sidekiq가 될지 아닌지 알 수 없기 때문에 이 방식이 마음에 들지 않습니다. 우리의 배포 환경에서는 unicorn master → fork → sidekiq worker 구조를 사용합니다. 포크 시점에 application.rb는 이미 파싱되어 있었습니다.

@kris.kotlarek 수정해 주셔서 감사합니다. 이제 Sidekiq가 Discourse 공식 플러그인과 함께 정상적으로 작동합니다 :+1: .

하지만 제 3자 플러그인 중 잡(job)을 사용하는 것들은 작동하지 않습니다 :cry: . 아마도 아래 커밋이 해당 플러그인의 Jobs:: 클래스에 적용되지 않아서 생긴 문제인 것 같습니다. :arrow_down:


작동하지 않는 3자 플러그인

맞습니다. 이제 Jobs::Onceoff, Jobs::Base, Jobs::Scheduled 모두 ::가 필요합니다.

discourse-whos-online을 수정하고 다른 플러그인들을 위한 풀 리퀘스트를 생성했습니다:

@kris.kotlarek 감사합니다 :partying_face: :+1: . zeitwerk 자동 로더에 대한 게시글을 확인해 보았습니다. 플러그인 개발 속도를 높이기 위해 개발용 플러그인 자동 재로딩 기능을 활성화할 계획이 있나요?

require 또는 require_dependency가 포함된 곳이 여전히 있지만, 상당수는 제거되었습니다.

이제 플러그인 코드가 자동으로 다시 로드될 것이라고 생각합니다. 확실하지는 않으니 조금 더 테스트해봐야겠지만, 긍정적인 느낌이 듭니다 :slight_smile:

문서대로 먼저 활성화해야 하는 것 같습니다. 개발 환경에서 시도해 보았지만 플러그인 변경 사항이 다시 로드되지 않았습니다 :cry:

정말 좋겠네요. 기대를 하고 있었는데, 말씀 덕분에 더 큰 희망을 갖게 되었습니다. 플러그인 개발자들이 이 기능을 좋아할 겁니다.