- ApplicationWorker
- 재시도
- 실패 처리
- Sidekiq 워커 연기
- Sidekiq 큐
- 대기열 네임스페이스
- 버전 관리
- 작업 크기
- 작업 가중치
- 테스트
- Sidekiq Redis 및 API와 상호 작용
Sidekiq 개발 가이드라인
우리는 백그라운드 작업 프로세서로 Sidekiq를 사용합니다. 이 안내서는 GitLab.com에서 잘 작동하고 기존 워커 클래스와 일관성을 유지하는 작업을 작성하는 데 사용됩니다. GitLab을 관리하는 정보는 Sidekiq 구성을 참조하세요.
다음 주제에 대한 자세한 내용이 포함된 페이지가 있습니다.
- 업데이트 간 호환성
- 작업 멱등성 및 작업 중복 제거
- 제한된 용량 워커: 지정된 동시성으로 계속해서 작업 수행
- 로깅
-
워커 속성
- 작업 긴급성은 대기열 및 실행 SLO를 지정합니다.
- 작업량 설명을 위한 리소스 경계 및 외부 의존성
- 기능 분류
- 데이터베이스 부하 분산
ApplicationWorker
모든 워커는 Sidekiq::Worker
대신 ApplicationWorker
를 포함해야 합니다. 이는 라우팅 규칙에 따라 큐를 자동으로 설정하고 편의 기능을 추가합니다.
재시도
Sidekiq은 각 재시도 사이에 백오프를 사용하여 25번의 재시도를 기본값으로 사용합니다. 25번의 재시도는 마지막 재시도가 첫 번째 시도 후 약 세 주 후에 발생하게 됩니다. 이는 작업이 예약되고 실행될 때 사이에 많은 일이 발생할 수 있다는 것을 의미합니다. 따라서 예약된 후 상태가 변경되어 워커가 25번 실패하지 않도록 주의해야 합니다. 예를 들어, 작업이 예약된 프로젝트가 삭제된 경우 작업이 실패해서는 안 됩니다.
다음과 같이 하세요:
def perform(project_id)
project = Project.find_by_id(project_id)
return unless project
# ...
end
대부분의 워커 - 특히 멱등 워커의 경우 - 25번 재시도의 기본값은 충분합니다. 우리의 많은 구식 워커들은 이전에 GitLab 애플리케이션 내에서의 기본값이었던 3번의 재시도를 선언합니다. 3번의 재시도는 몇 분 동안 이루어지므로 작업이 완전히 실패하기 쉽습니다.
아래 중 하나라도 해당되는 경우 재시도 회수를 낮출 수 있습니다.
- 워커가 외부 서비스에 연락을 취하고 전달 보장을 제공하지 않는 경우. 예를 들어, 웹훅.
- 워커가 멱등성이 없고 여러 번 실행되면 시스템을 일관성 없는 상태로 남길 수 있는 경우. 예를 들어, 시스템 노트를 게시하고 그런 다음 작업을 수행하는 워커: 두 번째 단계가 실패하고 워커가 재시도되면 시스템 노트가 다시 게시됩니다.
- 워커가 자주 실행되는 크론 작업인 경우. 예를 들어, 크론 작업이 매 시간 실행되면 한 시간을 넘어서 재시도할 필요가 없습니다.
한 워커의 각 재시도는 우리의 메트릭에서 실패로 처리됩니다. 항상 9번 실패하고 10번째에 성공하는 워커는 90%의 오류율을 갖게 됩니다.
Sentry에서 예외를 추적하지 않고 워커를 매뉴얼으로 재시도하려면 Gitlab::SidekiqMiddleware::RetryError
에서 상속된 예외 클래스를 사용하세요.
ServiceUnavailable = Class.new(::Gitlab::SidekiqMiddleware::RetryError)
def perform
...
raise ServiceUnavailable if external_service_unavailable?
end
실패 처리
일반적으로 실패는 Sidekiq 자체에 의해 처리되며, 앞에서 언급한 내장 재시도 메커니즘을 활용합니다. 작업을 재스케줄링할 수 있도록 예외가 발생하도록 허용해야 합니다.
작업이 모든 재시도 시도 후에 실패할 때 작업이 실패할 때 동작을 수행해야 하는 경우 sidekiq_retries_exhausted
메소드에 추가하세요.
sidekiq_retries_exhausted do |msg, ex|
project = Project.find(msg['args'].first)
project.perform_a_rollback # 영구적인 실패 처리
end
def perform(project_id)
project = Project.find(project_id)
project.some_action # 예외를 throw합니다
end
Sidekiq 워커 연기
Sidekiq 워커는 두 가지 방법으로 연기됩니다.
- 매뉴얼: 피처 플래그를 사용하여 특정 워커를 명시적으로 연기할 수 있습니다. 자세한 내용은 여기에서 찾을 수 있습니다.
-
자동: 배치 마이그레이션의 쓰로틀링 메커니즘과 유사하게, 데이터베이스 상태 지표가 Sidekiq 워커를 연기하는 데 사용됩니다.
자동 연기 메커니즘을 사용하려면, 워커는
gitlab_schema
,delay_by
(지연 시간), 테이블 (자동 진공 db 표시기에서 사용됨)를 이용해서defer_on_database_health_signal
을 호출해야 합니다.예시:
module Chaos class SleepWorker # rubocop:disable Scalability/IdempotentWorker include ApplicationWorker data_consistency :always sidekiq_options retry: 3 include ChaosQueue defer_on_database_health_signal :gitlab_main, [:users], 1.minute def perform(duration_s) Gitlab::Chaos.sleep(duration_s) end end end
연기된 작업에 대한 로그에는 원본을 나타내는 다음 내용이 포함됩니다.
-
job_status
:deferred
-
job_deferred_by
:feature_flag
또는database_health_check
Sidekiq 큐
이전에는 각 워커가 자체 큐를 가졌고, 워커 클래스 이름을 기반으로 자동으로 큐 이름이 설정되었습니다. ProcessSomethingWorker
라는 워커의 경우 큐 이름은 process_something
이었습니다. 이제 큐 라우팅 규칙을 사용하여 워커를 특정 큐로 라우팅할 수 있습니다. GDK에서는 새로운 워커가 default
이라는 큐로 라우팅됩니다.
어떤 큐를 사용하는지 확실하지 않다면, SomeWorker.queue
를 사용하여 찾을 수 있습니다. sidekiq_options queue: :some_queue
를 사용하여 큐 이름을 매뉴얼으로 재정의할 필요는 거의 없습니다.
새로운 워커를 추가한 후에는 bin/rake
gitlab:sidekiq:all_queues_yml:generate
를 실행하여 app/workers/all_queues.yml
또는 ee/app/workers/all_queues.yml
을 다시 생성하여 sidekiq-cluster에서 라우팅 규칙을 사용하지 않는 설치에서 선택할 수 있도록 해야 합니다. 가능한 변경 사항에 대한 자세한 정보는 epic 596를 참조하세요.
또한,
bin/rake gitlab:sidekiq:sidekiq_queues_yml:generate
를 실행하여 config/sidekiq_queues.yml
을 다시 생성해야 합니다.
대기열 네임스페이스
다른 worker는 대기열을 공유할 수 없지만, 대기열 네임스페이스를 공유할 수 있습니다.
worker에 대한 대기열 네임스페이스를 정의하면 해당 네임스페이스의 모든 worker 작업을 자동으로 처리하는 Sidekiq 프로세스를 시작할 수 있으며, 명시적으로 모든 대기열 이름을 나열할 필요가 없습니다. 예를 들어 sidekiq-cron
으로 관리되는 모든 worker가 cronjob
대기열 네임스페이스를 사용하는 경우, 이러한 유형의 스케줄링된 작업을 처리하는 특정 Sidekiq 프로세스를 실행할 수 있습니다. 나중에 cronjob
네임스페이스를 사용하는 새로운 worker가 추가되더라도 설정을 변경할 필요 없이(다시 시작된 후) Sidekiq 프로세스는 해당 worker의 작업도 처리합니다.
대기열 네임스페이스는 queue_namespace
DSL 클래스 메서드를 사용하여 설정할 수 있습니다.
class SomeScheduledTaskWorker
include ApplicationWorker
queue_namespace :cronjob
# ...
end
실제로는 이렇게 하면 SomeScheduledTaskWorker.queue
가 cronjob:some_scheduled_task
로 설정됩니다. 자주 사용되는 네임스페이스는 worker 클래스에 쉽게 포함시킬 수 있는 관련 모듈이 있으며, 이는 대기열 네임스페이스 외에도 다른 Sidekiq 옵션을 설정할 수 있습니다. 예를 들어 CronjobQueue
는 네임스페이스를 설정하지만, 재시도를 비활성화합니다.
bundle exec sidekiq
은 네임스페이스를 인식하며, --queue
(-q
) 옵션이 아닌 경우에는 config/sidekiq_queues.yml
의 :queues:
섹션에 대기열 이름 대신 네임스페이스가 제공되는 경우 해당 네임스페이스의 모든 대기열을 수신 대기합니다.
기존 네임스페이스에 worker를 추가하는 경우, 해당 네임스페이스를 처리하는 Sidekiq 프로세스의 사용 가능한 리소스가 적절하게 조정되지 않은 경우 이미 있는 worker의 작업에서 추가된 작업이 리소스를 소비하므로 주의해야 합니다.
버전 관리
각 Sidekiq worker 클래스에서 버전을 지정할 수 있습니다. 그럼 작업이 생성될 때 해당 버전이 전송됩니다.
class FooWorker
include ApplicationWorker
version 2
def perform(*args)
if job_version == 2
foo = args.first['foo']
else
foo = args.first
end
end
end
이 스키마에 따라 어떤 worker든 해당 worker의 이전 버전에서 인큐된 작업을 처리할 수 있다고 기대됩니다. 이는 worker가 받는 인수를 변경할 때 version
를 증가시켜야 하지만(또는 worker의 인수가 변경되는 경우 version 1
로 설정), 이전 버전의 인수로 인큐된 작업을 여전히 처리할 수 있어야 함을 의미합니다. worker의 perform
메서드에서는 특정 작업 버전을 분기하려면 self.job_version
을 읽을 수 있으며, 제공된 인수의 수나 유형을 읽을 수도 있습니다.
작업 크기
GitLab은 Sidekiq 작업 및 그 인수를 Redis에 저장합니다. 원본 크기가 100 KB보다 크면 Sidekiq 작업의 인수를 압축하여 과도한 메모리 사용을 피합니다.
압축 후에도 크기가 5 MB를 초과하면 작업을 예약할 때 ExceedLimitError
오류가 발생합니다.
이런 경우 Sidekiq에서 데이터를 사용할 다른 방법에 의존해야 합니다. 데이터를 다시 빌드하거나 작업을 예약하기 전에 객체 리포지터리에 데이터를 저장하고 작업 내에서 검색하는 등 가능한 해결책이 있습니다.
작업 가중치
일부 작업에는 선언된 가중치가 있습니다. 기본 실행 모드인 Sidekiq에서만 사용됩니다 - 기본 가중치(1)를 사용하는 sidekiq-cluster를 사용하는 경우 가중치가 고려되지 않습니다.
우리는 이미 무료로 sidekiq-cluster
를 사용하도록 이동하고 있습니다. 따라서 새로 추가된 worker는 가중치를 지정할 필요가 없습니다. 기본 가중치(1)를 사용할 수 있습니다.
테스트
각 Sidekiq worker는 기타 클래스와 마찬가지로 RSpec을 사용하여 테스트되어야 합니다. 이러한 테스트는 spec/workers
에 배치되어야 합니다.
Sidekiq Redis 및 API와 상호 작용
애플리케이션은 Sidekiq.redis
와 Sidekiq API의 상호 작용을 최소화해야 합니다. 일반적인 응용 프로그램 로직에서 이러한 상호 작용은 재사용을 위해 Sidekiq 미들웨어로 추상화되어야 합니다. Sidekiq 데이터 리포지터리와 애플리케이션 로직을 분리함으로써 GitLab 백그라운드 처리 설정을 수평으로 확장할 때 더 큰 자유도를 제공합니다.
이 규칙에는 마이그레이션 관련 로직 또는 관리 작업과 같은 예외 사항이 있을 수 있습니다.