InfoGrab Docs

백그라운드 작업(Background operations)

요약

이 프레임워크는 실험적이며 변경 사항이나 버그가 있을 수 있습니다. 백그라운드 작업은 GitLab 데이터베이스에서 대규모 데이터 작업을 수행하기 위한 프레임워크를 제공합니다. 릴리스에 연결된 일회성 데이터 마이그레이션의 경우 대신 일괄 백그라운드 마이그레이션을 사용하세요.

Warning

이 프레임워크는 실험적이며 변경 사항이나 버그가 있을 수 있습니다. 채택하기 전에 Slack의 #g_database_architecture에 문의하세요.

백그라운드 작업은 GitLab 데이터베이스에서 대규모 데이터 작업을 수행하기 위한 프레임워크를 제공합니다. 업그레이드 중에 한 번 완료될 때까지 실행되는 일괄 백그라운드 마이그레이션(BBM)과 달리, 백그라운드 작업은 반복되는 크론 스케줄 실행과 .enqueue API를 통한 온디맨드 프로그래밍 방식 실행을 모두 지원합니다.

릴리스에 연결된 일회성 데이터 마이그레이션의 경우 대신 일괄 백그라운드 마이그레이션을 사용하세요.

백그라운드 작업을 사용해야 할 때#

단일 실행 창 내에서 완료할 수 없는 큰 테이블에서 데이터 작업을 수행해야 할 때 백그라운드 작업을 사용합니다.

백그라운드 작업이 적합한 경우:

  • 반복 스케줄로 행을 삭제하거나 업데이트할 때 (예: 오래된 데이터 제거).
  • 업그레이드 중에만이 아닌 지속적으로 실행되어야 하는 지속적인 데이터 정리를 수행할 때.
  • 애플리케이션 코드에서 프로그래밍 방식으로 일회성 대규모 데이터 작업을 트리거할 때.
  • 단일 패스가 안전한 실행 시간을 초과하는 트래픽이 많은 테이블에서 작업할 때.

스키마 변경이나 일반 마이그레이션 시간 제한 내에 완료될 수 있는 작업에는 백그라운드 작업을 사용하지 마세요.

백그라운드 작업 작동 방식#

백그라운드 작업은 perform 메서드를 정의하는 Gitlab::BackgroundOperation::BaseOperationWorker의 서브클래스입니다. 작업은 두 가지 방법으로 스케줄할 수 있습니다:

  • 크론 기반: 크론 작업(Database::BackgroundOperation::CronEnqueueWorker)이 구성된 스케줄로 작업을 트리거합니다.
  • 온디맨드: 애플리케이션 코드가 Worker.enqueue를 호출하여 프로그래밍 방식으로 작업을 생성하고 실행합니다.

각 호출은 커서 기반 키셋 반복을 사용하여 행의 배치를 처리하고, 마지막 실행이 중단된 위치부터 계속하며, 사용자 정의 로직에 서브배치를 넘겨줍니다.

모든 작업 클래스는 Gitlab::BackgroundOperation 네임스페이스에 정의되어야 합니다. 파일은 lib/gitlab/background_operation/ 디렉토리에 배치합니다.

실행 메커니즘#

백그라운드 작업은 BBM과 동일한 실행 파이프라인을 따릅니다 (Scheduler → Orchestrator → Runner → Executor). 자세한 내용은 BBM 실행 메커니즘을 참조하세요. 주요 차이점은 백그라운드 작업이 기본 키 범위 배치 대신 커서 기반 키셋 페이지네이션을 사용한다는 것입니다.

워커 테이블은 잠금 없는 동시 실행을 위해 리스트 파티셔닝됩니다. 완료되지 않은 상태에 대한 부분 고유 인덱스는 동일한 구성으로 중복 작업이 생성되는 것을 방지합니다.

조직 범위 및 셀 로컬 테이블#

두 가지 테이블 변형이 있습니다:

  • background_operation_workers는 조직 범위 작업을 저장합니다. 이 레코드는 organization_iduser_id가 필요하며, 사용자가 작업을 트리거할 때 생성됩니다 (예: user: 매개변수와 함께 .enqueue를 통해).
  • background_operation_workers_cell_local은 조직 컨텍스트 없이 셀 로컬 작업을 저장합니다. 이 레코드는 일반적으로 시스템 전체 유지 관리 작업을 수행하는 크론 작업에 의해 생성됩니다.

동일한 분리가 작업 테이블(background_operation_jobsbackground_operation_jobs_cell_local)에도 적용됩니다.

조직이 이동할 때 발생하는 일#

조직이 다른 셀로 이동하면, background_operation_workers의 레코드는 organization_id로 범위가 지정되기 때문에 함께 이동합니다. 진행 중인 작업은 저장된 커서 위치에서 새 셀에서 재개됩니다. background_operation_workers_cell_local의 셀 로컬 레코드는 조직 이동의 영향을 받지 않습니다 — 생성된 셀에 남아있습니다.

중복 감지#

완료되지 않은 상태(queued, active, on_hold)에 대한 부분 고유 인덱스는 동일한 구성으로 여러 작업이 동시에 실행되는 것을 방지합니다. 이는 동일한 테이블과 컬럼 범위에서 중복 작업을 실행하면 중복 작업이 발생하고, 데이터베이스 부하가 증가하며, 동일한 행의 동시 변경으로 인한 데이터 무결성 문제가 생길 수 있기 때문에 필요합니다.

.enqueue를 사용할 때, 프레임워크는 동일한 구성(job_class_name, table_name, column_name, job_arguments)으로 기존의 완료되지 않은 작업이 있는지 확인합니다. 중복이 발견되면 큐 추가가 건너뛰어지고 경고가 기록됩니다. finished 또는 failed 상태의 작업은 새 큐 추가를 차단하지 않습니다.

이전 진행 상황에서 재개#

동일한 job_class_name, table_name, column_name, job_arguments로 새 작업이 큐에 추가될 때(중복 감지 참조), 프레임워크는 처음부터 테이블 전체를 다시 스캔하는 대신 자동으로 이전 작업이 중단된 위치부터 재개합니다.

모든 실행에서 테이블의 처음부터 시작해야 하는 작업은 작업 클래스에서 reset_cursor!를 선언할 수 있습니다:

class MyOperation < BaseOperationWorker
  operation_name :delete_all
  cursor :id
  reset_cursor!

  def perform
    each_sub_batch { |sub_batch| sub_batch.delete_all }
  end
end

또는 .enqueue를 호출할 때 명시적인 min_cursor를 전달할 수도 있습니다.

멱등성#

백그라운드 작업 워커는 Sidekiq 내에서 실행됩니다. 작업은 멱등해야 합니다. 동일한 행을 재처리해도 동일한 결과가 나오도록 perform 메서드를 설계합니다.

스로틀링 및 격리#

백그라운드 작업은 BBM과 동일한 데이터베이스 상태 확인격리 제약을 공유합니다.

방법#

크론을 통한 스케줄 (반복 작업)#

고정 간격으로 무기한 실행해야 하는 작업(예: 매시간 만료된 데이터 제거)에 크론 스케줄링을 사용합니다.

1. 작업 클래스 정의#

lib/gitlab/background_operation/에 파일을 생성합니다:

# frozen_string_literal: true

module Gitlab
  module BackgroundOperation
    class UsersDeleteUnconfirmedSecondaryEmails < BaseOperationWorker
      operation_name :delete_all
      feature_category :user_management
      cursor :id

      def perform
        each_sub_batch do |sub_batch|
          sub_batch
            .where('created_at < ? AND confirmed_at IS NULL', created_cut_off)
            .delete_all
        end
      end

      private

      def created_cut_off
        ApplicationSetting::USERS_UNCONFIRMED_SECONDARY_EMAILS_DELETE_AFTER_DAYS.days.ago
      end
    end
  end
end

주요 DSL 메서드:

  • operation_name: SQL 작업을 설명하는 심볼 (예: :delete_all, :update_all). 계측에 사용됩니다.
  • feature_category: 이 작업을 소유하는 기능 카테고리.
  • cursor: 키셋 페이지네이션에 사용되는 하나 이상의 컬럼 이름. 테이블의 기본 키를 사용합니다. 복합 기본 키의 경우: cursor :partition_id, :id.

2. 크론 작업 구성#

config/initializers/1_settings.rb에 크론 스케줄을 추가합니다:

Settings.cron_jobs['bbo_users_delete_unconfirmed_secondary'] ||= {}
Settings.cron_jobs['bbo_users_delete_unconfirmed_secondary']['cron'] ||= '0 * * * *'
Settings.cron_jobs['bbo_users_delete_unconfirmed_secondary']['job_class'] = 'Database::BackgroundOperation::CronEnqueueWorker'
Settings.cron_jobs['bbo_users_delete_unconfirmed_secondary']['args'] = {
  'job_class_name' => 'UsersDeleteUnconfirmedSecondaryEmails',
  'table_name' => 'emails',
  'column_name' => 'id'
}

구성 필드:

  • cron: 스케줄을 위한 표준 크론 표현식.
  • job_class: 항상 Database::BackgroundOperation::CronEnqueueWorker.
  • args: 다음을 포함하는 해시:
    • job_class_name: 작업의 클래스 이름 (Gitlab::BackgroundOperation:: 접두사 제외).
    • table_name: 반복할 데이터베이스 테이블.
    • column_name: 커서 기반 반복에 사용되는 컬럼.

큐를 통한 스케줄 (온디맨드 작업)#

애플리케이션 로직이나 서비스에 의해 시작되는 대량 정리와 같이 프로그래밍 방식으로 트리거되는 작업에 .enqueue를 사용합니다.

Gitlab::Database::BackgroundOperation::Worker.enqueue(
  'MyOperationClass',
  'target_table',
  'id',
  job_arguments: %w[arg1 arg2],
  user: current_user
)

매개변수:

  • job_class_name: 작업 클래스 이름.
  • table_name: 반복할 데이터베이스 테이블.
  • column_name: 커서 컬럼.
  • job_arguments (선택 사항): 문자열 인수 배열. 기본값: [].
  • user: 작업을 시작하는 사용자. 레코드에 user_idorganization_id를 설정합니다.

프레임워크는 자동으로 중복을 확인하고, pg_class를 통해 total_tuple_count를 추정하고, 기본 배치 매개변수를 설정하고, 테이블의 gitlab_schema를 기반으로 올바른 데이터베이스 연결을 해석합니다.

조직 컨텍스트 없는 작업의 경우 WorkerCellLocal을 사용합니다:

Gitlab::Database::BackgroundOperation::WorkerCellLocal.enqueue(
  'MyOperationClass',
  'target_table',
  'id'
)

모니터링#

백그라운드 작업은 관찰 가능성을 위해 구조화된 로그와 Prometheus 메트릭을 내보냅니다.

구조화된 로그#

프레임워크는 상태 전환 및 배치 크기 최적화 시 Gitlab::AppLogger에 이벤트를 기록합니다. 다음 message 값으로 필터링합니다:

  • background_operation_worker_transition_event: 작업이 상태를 변경할 때 기록됩니다 (예: queuedactive, activefinished). job_class_name, table_name, previous_state, new_state를 포함합니다.
  • background_operation_job_transition_event: 개별 작업이 상태를 변경할 때 기록됩니다. 실패 시 attempts, exception_class, exception_message를 포함합니다.
  • background_operation_worker_optimization_event: 배치 크기가 조정될 때 기록됩니다. old_batch_sizenew_batch_size를 포함합니다.

Prometheus 메트릭#

각 작업 실행 후 다음 메트릭이 내보내집니다:

메트릭 타입 설명
background_operation_job_batch_size Gauge 현재 배치 크기
background_operation_job_sub_batch_size Gauge 현재 서브배치 크기
background_operation_job_interval_seconds Gauge 배치 간 간격
background_operation_job_duration_seconds Gauge 마지막 작업의 기간
background_operation_job_updated_tuples_total Counter 누적 처리된 튜플 수
background_operation_job_query_duration_seconds Histogram 작업당 쿼리 타이밍
background_operation_worker_migrated_tuples_total Gauge 지금까지 마이그레이션된 총 튜플 수
background_operation_worker_total_tuple_count Gauge 처리할 예상 총 튜플 수
background_operation_worker_last_update_time_seconds Gauge 마지막 업데이트의 Unix 타임스탬프

모든 메트릭은 migration_idmigration_identifier (job_class_name/table_name.column_name)로 레이블이 지정됩니다.

백그라운드 작업(Background operations)

원문 보기
요약

이 프레임워크는 실험적이며 변경 사항이나 버그가 있을 수 있습니다. 백그라운드 작업은 GitLab 데이터베이스에서 대규모 데이터 작업을 수행하기 위한 프레임워크를 제공합니다. 릴리스에 연결된 일회성 데이터 마이그레이션의 경우 대신 일괄 백그라운드 마이그레이션을 사용하세요.

Warning

이 프레임워크는 실험적이며 변경 사항이나 버그가 있을 수 있습니다. 채택하기 전에 Slack의 #g_database_architecture에 문의하세요.

백그라운드 작업은 GitLab 데이터베이스에서 대규모 데이터 작업을 수행하기 위한 프레임워크를 제공합니다. 업그레이드 중에 한 번 완료될 때까지 실행되는 일괄 백그라운드 마이그레이션(BBM)과 달리, 백그라운드 작업은 반복되는 크론 스케줄 실행과 .enqueue API를 통한 온디맨드 프로그래밍 방식 실행을 모두 지원합니다.

릴리스에 연결된 일회성 데이터 마이그레이션의 경우 대신 일괄 백그라운드 마이그레이션을 사용하세요.

백그라운드 작업을 사용해야 할 때#

단일 실행 창 내에서 완료할 수 없는 큰 테이블에서 데이터 작업을 수행해야 할 때 백그라운드 작업을 사용합니다.

백그라운드 작업이 적합한 경우:

  • 반복 스케줄로 행을 삭제하거나 업데이트할 때 (예: 오래된 데이터 제거).
  • 업그레이드 중에만이 아닌 지속적으로 실행되어야 하는 지속적인 데이터 정리를 수행할 때.
  • 애플리케이션 코드에서 프로그래밍 방식으로 일회성 대규모 데이터 작업을 트리거할 때.
  • 단일 패스가 안전한 실행 시간을 초과하는 트래픽이 많은 테이블에서 작업할 때.

스키마 변경이나 일반 마이그레이션 시간 제한 내에 완료될 수 있는 작업에는 백그라운드 작업을 사용하지 마세요.

백그라운드 작업 작동 방식#

백그라운드 작업은 perform 메서드를 정의하는 Gitlab::BackgroundOperation::BaseOperationWorker의 서브클래스입니다. 작업은 두 가지 방법으로 스케줄할 수 있습니다:

  • 크론 기반: 크론 작업(Database::BackgroundOperation::CronEnqueueWorker)이 구성된 스케줄로 작업을 트리거합니다.
  • 온디맨드: 애플리케이션 코드가 Worker.enqueue를 호출하여 프로그래밍 방식으로 작업을 생성하고 실행합니다.

각 호출은 커서 기반 키셋 반복을 사용하여 행의 배치를 처리하고, 마지막 실행이 중단된 위치부터 계속하며, 사용자 정의 로직에 서브배치를 넘겨줍니다.

모든 작업 클래스는 Gitlab::BackgroundOperation 네임스페이스에 정의되어야 합니다. 파일은 lib/gitlab/background_operation/ 디렉토리에 배치합니다.

실행 메커니즘#

백그라운드 작업은 BBM과 동일한 실행 파이프라인을 따릅니다 (Scheduler → Orchestrator → Runner → Executor). 자세한 내용은 BBM 실행 메커니즘을 참조하세요. 주요 차이점은 백그라운드 작업이 기본 키 범위 배치 대신 커서 기반 키셋 페이지네이션을 사용한다는 것입니다.

워커 테이블은 잠금 없는 동시 실행을 위해 리스트 파티셔닝됩니다. 완료되지 않은 상태에 대한 부분 고유 인덱스는 동일한 구성으로 중복 작업이 생성되는 것을 방지합니다.

조직 범위 및 셀 로컬 테이블#

두 가지 테이블 변형이 있습니다:

  • background_operation_workers는 조직 범위 작업을 저장합니다. 이 레코드는 organization_iduser_id가 필요하며, 사용자가 작업을 트리거할 때 생성됩니다 (예: user: 매개변수와 함께 .enqueue를 통해).
  • background_operation_workers_cell_local은 조직 컨텍스트 없이 셀 로컬 작업을 저장합니다. 이 레코드는 일반적으로 시스템 전체 유지 관리 작업을 수행하는 크론 작업에 의해 생성됩니다.

동일한 분리가 작업 테이블(background_operation_jobsbackground_operation_jobs_cell_local)에도 적용됩니다.

조직이 이동할 때 발생하는 일#

조직이 다른 셀로 이동하면, background_operation_workers의 레코드는 organization_id로 범위가 지정되기 때문에 함께 이동합니다. 진행 중인 작업은 저장된 커서 위치에서 새 셀에서 재개됩니다. background_operation_workers_cell_local의 셀 로컬 레코드는 조직 이동의 영향을 받지 않습니다 — 생성된 셀에 남아있습니다.

중복 감지#

완료되지 않은 상태(queued, active, on_hold)에 대한 부분 고유 인덱스는 동일한 구성으로 여러 작업이 동시에 실행되는 것을 방지합니다. 이는 동일한 테이블과 컬럼 범위에서 중복 작업을 실행하면 중복 작업이 발생하고, 데이터베이스 부하가 증가하며, 동일한 행의 동시 변경으로 인한 데이터 무결성 문제가 생길 수 있기 때문에 필요합니다.

.enqueue를 사용할 때, 프레임워크는 동일한 구성(job_class_name, table_name, column_name, job_arguments)으로 기존의 완료되지 않은 작업이 있는지 확인합니다. 중복이 발견되면 큐 추가가 건너뛰어지고 경고가 기록됩니다. finished 또는 failed 상태의 작업은 새 큐 추가를 차단하지 않습니다.

이전 진행 상황에서 재개#

동일한 job_class_name, table_name, column_name, job_arguments로 새 작업이 큐에 추가될 때(중복 감지 참조), 프레임워크는 처음부터 테이블 전체를 다시 스캔하는 대신 자동으로 이전 작업이 중단된 위치부터 재개합니다.

모든 실행에서 테이블의 처음부터 시작해야 하는 작업은 작업 클래스에서 reset_cursor!를 선언할 수 있습니다:

class MyOperation < BaseOperationWorker
  operation_name :delete_all
  cursor :id
  reset_cursor!

  def perform
    each_sub_batch { |sub_batch| sub_batch.delete_all }
  end
end

또는 .enqueue를 호출할 때 명시적인 min_cursor를 전달할 수도 있습니다.

멱등성#

백그라운드 작업 워커는 Sidekiq 내에서 실행됩니다. 작업은 멱등해야 합니다. 동일한 행을 재처리해도 동일한 결과가 나오도록 perform 메서드를 설계합니다.

스로틀링 및 격리#

백그라운드 작업은 BBM과 동일한 데이터베이스 상태 확인격리 제약을 공유합니다.

방법#

크론을 통한 스케줄 (반복 작업)#

고정 간격으로 무기한 실행해야 하는 작업(예: 매시간 만료된 데이터 제거)에 크론 스케줄링을 사용합니다.

1. 작업 클래스 정의#

lib/gitlab/background_operation/에 파일을 생성합니다:

# frozen_string_literal: true

module Gitlab
  module BackgroundOperation
    class UsersDeleteUnconfirmedSecondaryEmails < BaseOperationWorker
      operation_name :delete_all
      feature_category :user_management
      cursor :id

      def perform
        each_sub_batch do |sub_batch|
          sub_batch
            .where('created_at < ? AND confirmed_at IS NULL', created_cut_off)
            .delete_all
        end
      end

      private

      def created_cut_off
        ApplicationSetting::USERS_UNCONFIRMED_SECONDARY_EMAILS_DELETE_AFTER_DAYS.days.ago
      end
    end
  end
end

주요 DSL 메서드:

  • operation_name: SQL 작업을 설명하는 심볼 (예: :delete_all, :update_all). 계측에 사용됩니다.
  • feature_category: 이 작업을 소유하는 기능 카테고리.
  • cursor: 키셋 페이지네이션에 사용되는 하나 이상의 컬럼 이름. 테이블의 기본 키를 사용합니다. 복합 기본 키의 경우: cursor :partition_id, :id.

2. 크론 작업 구성#

config/initializers/1_settings.rb에 크론 스케줄을 추가합니다:

Settings.cron_jobs['bbo_users_delete_unconfirmed_secondary'] ||= {}
Settings.cron_jobs['bbo_users_delete_unconfirmed_secondary']['cron'] ||= '0 * * * *'
Settings.cron_jobs['bbo_users_delete_unconfirmed_secondary']['job_class'] = 'Database::BackgroundOperation::CronEnqueueWorker'
Settings.cron_jobs['bbo_users_delete_unconfirmed_secondary']['args'] = {
  'job_class_name' => 'UsersDeleteUnconfirmedSecondaryEmails',
  'table_name' => 'emails',
  'column_name' => 'id'
}

구성 필드:

  • cron: 스케줄을 위한 표준 크론 표현식.
  • job_class: 항상 Database::BackgroundOperation::CronEnqueueWorker.
  • args: 다음을 포함하는 해시:
    • job_class_name: 작업의 클래스 이름 (Gitlab::BackgroundOperation:: 접두사 제외).
    • table_name: 반복할 데이터베이스 테이블.
    • column_name: 커서 기반 반복에 사용되는 컬럼.

큐를 통한 스케줄 (온디맨드 작업)#

애플리케이션 로직이나 서비스에 의해 시작되는 대량 정리와 같이 프로그래밍 방식으로 트리거되는 작업에 .enqueue를 사용합니다.

Gitlab::Database::BackgroundOperation::Worker.enqueue(
  'MyOperationClass',
  'target_table',
  'id',
  job_arguments: %w[arg1 arg2],
  user: current_user
)

매개변수:

  • job_class_name: 작업 클래스 이름.
  • table_name: 반복할 데이터베이스 테이블.
  • column_name: 커서 컬럼.
  • job_arguments (선택 사항): 문자열 인수 배열. 기본값: [].
  • user: 작업을 시작하는 사용자. 레코드에 user_idorganization_id를 설정합니다.

프레임워크는 자동으로 중복을 확인하고, pg_class를 통해 total_tuple_count를 추정하고, 기본 배치 매개변수를 설정하고, 테이블의 gitlab_schema를 기반으로 올바른 데이터베이스 연결을 해석합니다.

조직 컨텍스트 없는 작업의 경우 WorkerCellLocal을 사용합니다:

Gitlab::Database::BackgroundOperation::WorkerCellLocal.enqueue(
  'MyOperationClass',
  'target_table',
  'id'
)

모니터링#

백그라운드 작업은 관찰 가능성을 위해 구조화된 로그와 Prometheus 메트릭을 내보냅니다.

구조화된 로그#

프레임워크는 상태 전환 및 배치 크기 최적화 시 Gitlab::AppLogger에 이벤트를 기록합니다. 다음 message 값으로 필터링합니다:

  • background_operation_worker_transition_event: 작업이 상태를 변경할 때 기록됩니다 (예: queuedactive, activefinished). job_class_name, table_name, previous_state, new_state를 포함합니다.
  • background_operation_job_transition_event: 개별 작업이 상태를 변경할 때 기록됩니다. 실패 시 attempts, exception_class, exception_message를 포함합니다.
  • background_operation_worker_optimization_event: 배치 크기가 조정될 때 기록됩니다. old_batch_sizenew_batch_size를 포함합니다.

Prometheus 메트릭#

각 작업 실행 후 다음 메트릭이 내보내집니다:

메트릭 타입 설명
background_operation_job_batch_size Gauge 현재 배치 크기
background_operation_job_sub_batch_size Gauge 현재 서브배치 크기
background_operation_job_interval_seconds Gauge 배치 간 간격
background_operation_job_duration_seconds Gauge 마지막 작업의 기간
background_operation_job_updated_tuples_total Counter 누적 처리된 튜플 수
background_operation_job_query_duration_seconds Histogram 작업당 쿼리 타이밍
background_operation_worker_migrated_tuples_total Gauge 지금까지 마이그레이션된 총 튜플 수
background_operation_worker_total_tuple_count Gauge 처리할 예상 총 튜플 수
background_operation_worker_last_update_time_seconds Gauge 마지막 업데이트의 Unix 타임스탬프

모든 메트릭은 migration_idmigration_identifier (job_class_name/table_name.column_name)로 레이블이 지정됩니다.