- ApplicationWorker
- 샤딩
- 재시도
- 실패 처리
- Sidekiq 작업자 지연
- Sidekiq 큐
- 큐 네임스페이스
- 버전 관리
- 작업 크기
- 작업 가중치
- 테스트
- Sidekiq Redis 및 API와 상호작용하기
Sidekiq 개발 지침
우리는 Sidekiq를 백그라운드 작업 프로세서로 사용합니다. 이 가이드는 GitLab.com에서 잘 작동하고 기존의 워커 클래스와 일치하는 작업을 작성하기 위한 것입니다. GitLab을 관리하는 방법에 대한 정보는 Sidekiq 구성을 참조하세요.
다음 주제에 대한 추가 세부정보가 포함된 페이지가 있습니다:
-
-
작업 긴급성은 큐잉 및 실행 SLO를 지정합니다.
-
리소스 경계 및 외부 종속성은 작업량을 설명하기 위해 사용됩니다.
-
기능 범주화
-
데이터베이스 로드 밸런싱
-
ApplicationWorker
모든 워커는 Sidekiq::Worker
대신 ApplicationWorker
를 포함해야 하며, 이는 몇 가지 편리한 메서드를 추가하고 라우팅 규칙에 따라 큐를 자동으로 설정합니다.
샤딩
모든 Sidekiq API 호출은 샤딩을 고려해야 합니다. 이를 위해서는, Sidekiq::Client.via
블록 내에서 Sidekiq API를 사용하여 정확한 Sidekiq.redis
풀을 보장해야 합니다.
적절한 Redis 풀은 Gitlab::SidekiqSharding::Router.get_shard_instance
메서드를 호출하여 얻습니다.
pool_name, pool = Gitlab::SidekiqSharding::Router.get_shard_instance(worker_class.sidekiq_options['store'])
Sidekiq::Client.via(pool) do
...
end
라우팅되지 않은 Sidekiq 호출은 모든 API 요청, 서버 측의 Sidekiq 작업 및 테스트에서 검증자가 잡습니다. 우리는 Gitlab::SidekiqSharding::Router
를 이용하여 애플리케이션 로직을 작성하는 것을 추천합니다. 그러나 샤딩이 출시되지 않은 기능이므로, 컴포넌트가 GitLab.com에 영향을 미치지 않는 경우, 아래와 같이 .allow_unrouted_sidekiq_calls
범위 내에서 실행하는 것이 허용됩니다.
# 이 경우 라우팅되지 않은 Sidekiq 호출을 허용하는 이유를 설명하는 주석 추가
Gitlab::SidekiqSharding::Validator.allow_unrouted_sidekiq_calls do
# 여러분의 라우팅되지 않은 논리
end
과거의 예로는 Geo Rake 작업에서 allow_unrouted_sidekiq_calls
를 사용한 것이 있습니다. 이는 GitLab.com에 영향을 미치지 않기 때문입니다. 그러나 개발자는 가능한 경우 샤드 인식 코드를 작성해야 하며, 이는 샤딩이 자체 관리 사용자에게 기능으로 출시되기 위한 전제 조건입니다.
재시도
Sidekiq는 25회 재시도를 기본값으로 사용하며, 각 재시도 간 대기 시간이 있습니다. 25회의 재시도는 마지막 재시도가 첫 번째 시도 약 3주 후에 발생함을 의미합니다(모든 24회의 재시도가 실패한 경우).
이는 작업이 예약되고 실행되는 사이에 많은 일이 발생할 수 있다는 것을 의미합니다. 그러므로 우리는 작업자가 예약된 후 상태가 변경될 때 25번 실패하지 않도록 작업자를 보호해야 합니다. 예를 들어, 작업이 예약된 프로젝트가 삭제되면 작업은 실패해야 하지 않습니다.
대신에:
def perform(project_id)
project = Project.find(project_id)
# ...
end
다음과 같이 작성합니다:
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가 작업을 다시 예약할 수 있도록 예외가 발생하도록 허용해야 합니다.
모든 재시도 시도가 실패한 후 작업이 실패했을 때 조치를 취해야 하는 경우, sidekiq_retries_exhausted
메서드에 이를 추가합니다.
sidekiq_retries_exhausted do |msg, ex|
project = Project.find_by_id(msg['args'].first)
return unless project
project.perform_a_rollback # 영구적인 실패 처리
end
def perform(project_id)
project = Project.find_by_id(project_id)
return unless project
project.some_action # 예외 발생
end
Sidekiq 작업자 지연
Sidekiq 작업자는 두 가지 방법으로 지연됩니다,
-
수동: 기능 플래그를 사용하여 특정 작업자를 명시적으로 지연시킬 수 있으며, 더 많은 세부정보는 여기를 참조하시기 바랍니다.
-
자동: 배치 마이그레이션에서의 스로틀링 메커니즘과 유사하게, 데이터베이스 건강 지표를 사용하여 Sidekiq 작업자를 지연시킵니다.
자동 지연 메커니즘을 사용하려면, 작업자가
defer_on_database_health_signal
을 호출하여gitlab_schema
,delay_by
(지연 시간) 및 테이블 (자동 진공 데이터베이스 지표에 의해 사용됨)을 매개변수로 지정해야 합니다.예시:
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
에서 라우팅 규칙을 사용하지 않는 설치에서 인식할 수 있습니다. 잠재적인 변경 사항에 대한 자세한 내용은 에픽 596을 참조하세요.
또한, bin/rake gitlab:sidekiq:sidekiq_queues_yml:generate
를 실행하여 config/sidekiq_queues.yml
을 다시 생성합니다.
큐 네임스페이스
서로 다른 작업자는 큐를 공유할 수는 없지만, 큐 네임스페이스는 공유할 수 있습니다.
작업자를 위한 큐 네임스페이스를 정의하면, 모든 작업자를 위해 자동으로 작업을 처리하는 Sidekiq 프로세스를 시작할 수 있으며, 모든 큐 이름을 명시적으로 나열할 필요가 없습니다. 예를 들어, sidekiq-cron
에 의해 관리되는 모든 작업자가 cronjob
큐 네임스페이스를 사용하는 경우, 이러한 종류의 예약 작업을 위해 특정한 Sidekiq 프로세스를 생성할 수 있습니다. 나중에 cronjob
네임스페이스를 사용하는 새로운 작업자가 추가되면, 해당 작업에는 영향을 주지 않으면서도(재시작된 후) Sidekiq 프로세스가 자동으로 작업을 처리합니다.
큐 네임스페이스는 queue_namespace
DSL 클래스 메서드를 사용하여 설정할 수 있습니다:
class SomeScheduledTaskWorker
include ApplicationWorker
queue_namespace :cronjob
# ...
end
이 과정에서 SomeScheduledTaskWorker.queue
는 cronjob:some_scheduled_task
로 설정됩니다. 일반적으로 사용되는 네임스페이스는 작업자 클래스에 쉽게 포함될 수 있는 자체 concern 모듈이 있으며, 이는 큐 네임스페이스 외에도 다른 Sidekiq 옵션을 설정할 수 있습니다. 예를 들어, CronjobQueue
는 네임스페이스를 설정하지만, 재시도를 비활성화하는 것도 포함됩니다.
bundle exec sidekiq
는 네임스페이스를 인식하며, 네임스페이스 이름으로 간단한 큐 이름 대신 --queue
(-q
) 옵션이나 config/sidekiq_queues.yml
의 :queues:
섹션에서 제공될 때 모든 큐를 듣습니다.
기존 네임스페이스에 작업자를 추가할 때는 주의가 필요합니다. 추가된 작업은 이미 존재하던 작업자의 작업에서 리소스를 소모하므로, 네임스페이스를 처리하는 Sidekiq 프로세스의 리소스가 적절하게 조정되지 않으면 문제가 발생할 수 있습니다.
버전 관리
각 Sidekiq 작업자 클래스에서 버전을 지정할 수 있습니다.
이것은 작업이 생성될 때 함께 전송됩니다.
class FooWorker
include ApplicationWorker
version 2
def perform(*args)
if job_version == 2
foo = args.first['foo']
else
foo = args.first
end
end
end
이 스키마에 따르면, 모든 작업자는 해당 작업자의 이전 버전이 대기열에 넣은 모든 작업을 처리할 수 있어야 합니다. 이는 작업자가 받는 인수를 변경할 때마다 version
을 증가시켜야 함을 의미합니다(작업자의 인수가 처음으로 변경되는 경우 version 1
로 설정). 하지만, 여전히 이전 버전의 인수로 큐에 넣은 작업을 처리할 수 있어야 합니다. 작업자의 perform
메서드에서 특정 작업 버전에 따라 분기하려면 self.job_version
을 읽거나 제공된 인수의 수나 유형을 읽을 수 있습니다.
작업 크기
GitLab은 Sidekiq 작업과 그 인수를 Redis에 저장합니다. 과도한 메모리 사용을 피하기 위해, Sidekiq 작업의 원본 크기가 100 KB를 초과하면 인수를 압축합니다.
압축 후에도 크기가 여전히 5 MB를 초과할 경우, 작업 예약 시 ExceedLimitError
오류가 발생합니다.
이런 경우, Sidekiq에서 데이터를 사용할 수 있게 하는 다른 방법에 의존해야 합니다. 가능한 해결 방법은 다음과 같습니다:
- 데이터베이스 또는 다른 곳에서 로드된 데이터를 사용하여 Sidekiq에서 데이터를 재구성합니다.
- 작업 예약 전에 데이터를 오브젝트 스토리지에 저장하고, 작업 내부에서 이를 가져옵니다.
작업 가중치
일부 작업에는 선언된 가중치가 있습니다. 이는 기본 실행 모드에서 Sidekiq를 실행할 때만 사용됩니다 -
sidekiq-cluster
를 사용하면 가중치가
고려되지 않습니다.
우리가 Free에서 sidekiq-cluster
사용으로 이동하고 있기 때문에, 새로 추가된 작업자는
가중치를 지정할 필요가 없습니다. 그들은 기본 가중치인 1을 사용할 수 있습니다.
테스트
각 Sidekiq 작업자는 다른 클래스와 마찬가지로 RSpec를 사용하여 테스트해야 합니다. 이러한
테스트는 spec/workers
에 배치해야 합니다.
Sidekiq Redis 및 API와 상호작용하기
어플리케이션은 Sidekiq.redis
및 Sidekiq API와의 상호작용을 최소화해야 합니다. 일반적인 어플리케이션 논리에서의 이러한 상호작용은 팀 간 재사용을 위해 Sidekiq 미들웨어로
추상화되어야 합니다. 어플리케이션 논리를 Sidekiq 데이터 저장소에서 분리함으로써 GitLab 백그라운드 처리 설정을 수평적으로 확장하는 데
더 큰 자유를 허용합니다.
이 규칙에 대한 몇 가지 예외는 마이그레이션 관련 논리 또는 관리 작업이 될 수 있습니다.