Sidekiq 개발 지침

우리는 Sidekiq을 백그라운드 작업 프로세서로 사용합니다. 이 가이드는 GitLab.com에서 잘 작동하고 기존 워커 클래스와 일관성을 유지하는 작업을 작성하기 위한 것입니다. GitLab을 관리하는 정보는 Sidekiq 구성을 참조하세요.

다음 주제에 대한 자세한 내용이 포함된 페이지가 있습니다.

  1. 업데이트 간 호환성
  2. 작업 멱등성 및 작업 중복
  3. 제한된 용량 워커: 지정된 병행성으로 지속적으로 작업 수행
  4. 로깅
  5. 워커 속성
    1. 작업 긴급성은 대기열 및 실행 SLO 지정
    2. 리소스 경계외부 종속성은 작업량을 설명하는 데 사용됨
    3. 기능 분류
    4. 데이터베이스 부하 분산

ApplicationWorker

모든 워커는 Sidekiq::Worker 대신 ApplicationWorker를 포함해야 합니다. ApplicationWorker라우팅 규칙에 따라 대기열을 자동으로 설정하는 몇 가지 편리한 메서드를 추가합니다.

재시도

Sidekiq은 25번의 재시도를 기본으로 사용하며 각 재시도 사이에 백오프가 있습니다. 25번의 재시도는 마지막 재시도가 첫 시도 후 약 세 주에 발생한다는 것을 의미합니다(이른바 이전 24번의 재시도가 모두 실패한 경우).

이는 작업이 예약되고 실행 사이에 많은 일이 발생할 수 있음을 의미합니다. 따라서 예약된 후 상태가 변경되어도 작업이 25번 실패하지 않도록 워커를 보호해야 합니다. 예를 들어, 작업이 예약된 프로젝트가 삭제된 경우 작업이 실패해서는 안 됩니다.

다음과 같이 하십시오: ruby def perform(project_id) project = Project.find_by_id(project_id) return unless project # ... end

대부분의 워커 - 특히 멱등 워커 - 에게는 25번의 기본 재시도가 충분합니다. 우리의 많은 예전 워커는 3번의 재시도를 선언하는데, 이것은 기존의 GitLab 애플리케이션 내에서 기본값이었습니다. 3번의 재시도는 몇 분 이내에 발생하기 때문에 작업은 완전히 실패하기 쉽습니다.

아래 중 하나라도 해당되면 낮은 재시도 횟수가 적용될 수 있습니다: 1. 워커가 외부 서비스에 연락하고 배달에 대한 보장을 제공하지 않는 경우. 예: 웹훅. 1. 워커가 멱등이 아니며 여러 번 실행될 경우 시스템이 일관성 없는 상태로 남을 수 있는 경우. 예: 시스템 노트를 게시하고 작업 실행하는 워커: 두 번째 단계가 실패하고 워커가 다시 시도되면 시스템 노트가 다시 게시됩니다. 1. 워커가 자주 실행되는 크론 작업인 경우. 예: 매 시간 크론 작업이 실행되면 한 시간을 넘어 재시도할 필요가 없기 때문에 한 번 이상의 동일한 작업이 실행될 필요가 없습니다.

워커의 각 재시도는 당사의 메트릭에서 실패로 계산됩니다. 항상 9번 실패하고 10번째에 성공하는 워커는 90%의 오류율을 가지게 됩니다.

Sentry에서 예외를 추적하지 않고 워커를 수동으로 재시도하려면 Gitlab::SidekiqMiddleware::RetryError에서 상속된 예외 클래스를 사용하십시오. ```ruby ServiceUnavailable = Class.new(::Gitlab::SidekiqMiddleware::RetryError)

def perform …

raise ServiceUnavailable if external_service_unavailable? end ```

실패 처리

실패는 일반적으로 Sidekiq 자체에 의해 처리되며 위에서 언급한 내장 재시도 메커니즘을 활용합니다. 작업이 재시도 시도를 모두 완료한 후에 실패하는 경우 조치를 취해야 하는 경우 예외를 발생시키도록 허용해야 합니다.

작업이 모든 재시도 시도 후에 실패하는 경우에 조치를 취해야 하는 경우 sidekiq_retries_exhausted 메서드에 추가하십시오. ```ruby 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 # 예외가 발생함 end ```

Sidekiq 워커 연기

Sidekiq 워커는 두 가지 방법으로 연기됩니다.

  1. 수동: 특정 워커를 명시적으로 연기하려면 기능 플래그를 사용할 수 있으며, 자세한 내용은 여기를 참조하십시오.
  2. 자동: 배치 마이그레이션의 쓰로틀링 메커니즘과 유사하게, 데이터베이스 상태 지표는 Sidekiq 워커를 연기하는 데 사용됩니다.

    자동 연기 메커니즘을 사용하려면 defer_on_database_health_signal을 호출하여 gitlab_schema, delay_by (지연 시간), 테이블 (자동 진공 db 지표에 사용됨)을 파라미터로 사용하여 워커가 참여하도록 해야 합니다.

    예: ```ruby 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을 다시 생성해야 합니다.

대기열 네임스페이스

서로 다른 워커는 같은 대기열을 공유할 수 없지만, 대기열 네임스페이스를 공유할 수 있습니다.

워커에 대한 대기열 네임스페이스를 정의하면 해당 네임스페이스의 모든 워커 작업을 자동으로 처리하는 Sidekiq 프로세스를 시작할 수 있습니다. 그러면 해당 네임스페이스의 모든 작업을 명시적으로 나열할 필요 없이 진행할 수 있습니다. 예를 들어, sidekiq-cron에 의해 관리되는 모든 워커가 cronjob 대기열 네임스페이스를 사용하면, 이러한 종류의 예약된 작업에 대해 특별히 Sidekiq 프로세스를 시작할 수 있습니다. 나중에 cronjob 네임스페이스를 사용하는 새로운 워커를 추가하면 해당 워커의 작업도 (재시작 후) 변경 구성 없이 Sidekiq 프로세스에서 처리합니다.

대기열 네임스페이스는 queue_namespace DSL 클래스 메서드를 사용하여 설정할 수 있습니다.

class SomeScheduledTaskWorker
  include ApplicationWorker

  queue_namespace :cronjob

  # ...
end

실제로, 이는 SomeScheduledTaskWorker.queuecronjob:some_scheduled_task로 설정합니다. 자주 사용되는 네임스페이스는 해당 워커 클래스에 쉽게 포함될 수 있는 관련 모듈을 갖고 있으며, 대기열 네임스페이스 외에도 다른 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을 증가시켜야 하지만, 또한 해당 워커가 이전 버전의 인수로 큐잉된 작업을 처리할 수 있는지 확인해야 합니다. 워커의 perform 메서드에서는 특정하게 작업 버전을 분기하려면 self.job_version을 읽을 수 있으며, 제공된 인수의 개수나 유형을 읽을 수 있습니다.

작업 크기

GitLab은 Sidekiq 작업과 해당 인수를 Redis에 저장합니다. 과도한 메모리 사용을 피하기 위해 원본 크기가 100 KB보다 큰 경우 Sidekiq 작업의 인수를 압축합니다.

압축 후에도 크기가 5MB를 초과하면 작업을 예약할 때 ExceedLimitError 오류가 발생합니다.

이런 경우에는 Sidekiq에서 데이터를 사용 가능하게 하는 다른 방법에 의존해야 합니다. 데이터베이스나 다른 곳에서 로드된 데이터로 Sidekiq에서 데이터를 재구성하거나, 작업을 예약하기 전에 객체 저장소에 데이터를 저장하고 작업 내에서 가져와야 합니다.

작업 가중치

일부 작업에는 가중치가 선언되어 있습니다. 이것은 기본 실행 모드에서 Sidekiq를 실행할 때에만 사용됩니다 - sidekiq-cluster를 사용하는 경우에는 가중치가 고려되지 않습니다.

우리는 무료로 sidekiq-cluster를 사용하도록 전환하고 있기 때문에, 새롭게 추가된 워커들은 가중치를 지정할 필요가 없습니다. 그들은 기본 가중치인 1을 사용할 수 있습니다.

테스트

각 Sidekiq 워커는 다른 클래스와 마찬가지로 RSpec을 사용하여 테스트되어야 합니다. 이러한 테스트는 spec/workers에 배치되어야 합니다.

Sidekiq Redis 및 API 상호 작용

애플리케이션은 Sidekiq.redis 및 Sidekiq APIs와의 상호 작용을 최소화해야 합니다. 일반적인 애플리케이션 로직에서 이러한 상호 작용은 팀 간 재사용을 위해 Sidekiq 미들웨어로 추상화되어야 합니다. Sidekiq 데이터 저장소와 애플리케이션 로직을 분리함으로써, GitLab 배경 처리 설정을 수평으로 확장할 때 더 많은 자유를 제공합니다.

이 규칙의 일부 예외는 마이그레이션 관련 로직이나 관리 작업입니다.