ES /docs

BulkableRepository missing .compact on nil IDs

RCA: Workarea with ids [nil, nil, nil, nil, nil, nil] does not match after 6 retries

Error Log#

Datadog Logs

text
Workarea with ids [nil, nil, nil, nil, nil, nil] does not match after 6 retries

Impact#

  • Service: cupixworks-worker
  • 발생 횟수: 1 (이 클러스터 기준), Datadog 14일 기간 동안 총 30건 발생
  • 최초 발생: 2026-04-14T04:28:05.584Z
  • 최근 발생: 2026-04-14T04:28:05.584Z

Root Cause Summary#

Workarea bulk update/delete 작업에서 일부 또는 전체 항목이 실패하면, BulkableRepository가 실패한 항목의 ID를 nil로 대체한 후 .compact를 호출하지 않고 BulkSavePartialJsonToFileWorker에 전달합니다. Worker는 nil ID 배열로 Workarea.where(id: [nil, nil, ...]) 쿼리를 실행하여 항상 0건을 반환하고, 6회 재시도 후 에러를 기록합니다. 이 문제를 수정한 커밋(ae00552f1, TSLA-12264)이 develop 브랜치에는 존재하지만 master 브랜치(프로덕션 배포 대상)에 머지되지 않아 수정이 프로덕션에 반영되지 않았습니다.

Technical Analysis#

Code Path#

  • Entry point: BulkableRepository#bulk! (app/repositories/concerns/bulkable_repository.rb:5)
  • Workarea mixin: BulkableRepository::Workarea (app/repositories/concerns/bulkable_repository/workarea.rb:1)
  • Failure point: BulkSavePartialJsonToFileWorker#perform (app/workers/bulk_save_partial_json_to_file_worker.rb:50-66)

1단계: Bulk 작업에서 invalid items 발생

BulkableRepository#bulk!에서 update/delete 작업 중 에러가 발생하면 해당 항목이 _invalid_items에 추가됩니다:

ruby
# app/repositories/concerns/bulkable_repository.rb:30-46 (update case)
_models.each_with_index do |model, index|
  _repository = self.class.new(review: _review, current_user: current_user, model: model, parent: _parent)
  _item = params[:items].find { |x| x[:id] == model.id }
  next if _item.nil?
  # ... update operations ...
  _repository.update(_item.except(:id))
rescue StandardError => e
  _invalid_items << {
    index: index
  }.merge(Cupix::Util::ErrorParser.parse_error(e))
  next
end

2단계: Nil ID 주입 및 worker 호출 (문제 지점)

실패한 항목의 인덱스에 nil을 설정한 후, 프로덕션 master 브랜치에서는 .compact 없이 worker에 전달합니다:

ruby
# app/repositories/concerns/bulkable_repository.rb:69-71 (master 브랜치 — 현재 프로덕션)
_invalid_items.each { |invalid_item| _model_ids[invalid_item[:index]] = nil }

bulk_save_changes_to_partial_json(_model_ids)  # .compact 없음 — nil이 포함된 배열 전달

develop 브랜치에는 수정이 존재합니다:

ruby
# app/repositories/concerns/bulkable_repository.rb:71 (develop 브랜치 — 미배포)
bulk_save_changes_to_partial_json(_model_ids.compact)  # .compact 추가

3단계: Worker에서 nil ID로 쿼리 실패

프로덕션 master 브랜치의 worker에는 nil guard가 없어, nil이 포함된 ids로 쿼리를 실행합니다:

ruby
# app/workers/bulk_save_partial_json_to_file_worker.rb:7-8 (master 브랜치 — 현재 프로덕션)
def perform(class_name, ids = [], options_json = {})
  # nil guard 없음 — ids가 그대로 사용됨

Workarea.where(id: [nil, nil, nil, nil, nil, nil])은 항상 0건을 반환하므로 models.count (0) != ids.count (6) 조건에 걸려 6회 재시도 후 에러가 기록됩니다:

ruby
# app/workers/bulk_save_partial_json_to_file_worker.rb:50-68 (프로덕션 코드)
$MAX_RETRIES.times do |retries|
  models = model_class.where(id: ids)  # nil ID로 쿼리 → 항상 0건
  if models.count != ids.count          # 0 != 6 → 항상 true
    delay = 0.1 * (2**retries)
    Cupix::Logger.warn("Retry #{retries + 1} - #{class_name} with ids #{ids} count does not match, retrying in #{delay} seconds", ...)
    sleep(delay)
    next
  else
    break
  end
end

if models.count != ids.count
  Cupix::Logger.error("#{class_name} with ids #{ids} does not match after #{$MAX_RETRIES} retries", ...)
  return false
end

4단계: 수정이 프로덕션에 미배포

TSLA-12264 커밋(ae00552f1, 2026-03-24)이 .compact 추가와 worker nil guard를 포함하지만, develop에만 존재하고 master에 머지되지 않았습니다:

text
$ git merge-base --is-ancestor ae00552f1 remotes/origin/master
→ NOT ancestor (fix NOT in master)

$ git branch -a --contains ae00552f1 | grep -E "develop|master"
→ * develop
→ (master not listed)

Log Evidence#

Datadog 검색 쿼리:

text
service:cupixworks-worker status:error "Workarea with ids" @environment:production

2026-04-14 에러 타임라인 (request_id: 68c8c0c1847f13ae4c3cee09):

Timestamp (UTC) Level Message
04:27:59.582Z warn Retry 1 - Workarea with ids [nil, nil, nil, nil, nil, nil] count does not match, retrying in 0.1 seconds
04:27:59.582Z warn Retry 2 - Workarea with ids [nil, nil, nil, nil, nil, nil] count does not match, retrying in 0.2 seconds
04:27:59.582Z warn Retry 3 - Workarea with ids [nil, nil, nil, nil, nil, nil] count does not match, retrying in 0.4 seconds
04:27:59.582Z warn Retry 4 - Workarea with ids [nil, nil, nil, nil, nil, nil] count does not match, retrying in 0.8 seconds
04:28:01.583Z warn Retry 5 - Workarea with ids [nil, nil, nil, nil, nil, nil] count does not match, retrying in 1.6 seconds
04:28:03.583Z warn Retry 6 - Workarea with ids [nil, nil, nil, nil, nil, nil] count does not match, retrying in 3.2 seconds
04:28:05.584Z error Workarea with ids [nil, nil, nil, nil, nil, nil] does not match after 6 retries

공통 속성:

text
Class: BulkSavePartialJsonToFileWorker
Function: perform
Host: ip-10-1-19-158.ap-southeast-2.compute.internal
Region: ap-southeast-2
Deploy Version: production-ap-southeast-2-20260411T0127Z0-9e255c3b-cupixworks

14일간 발생 패턴:

  • 총 30건의 error 레벨 발생 (2026-03-31 ~ 2026-04-14)
  • 주요 영향 리전: ap-southeast-2 (대부분), us-west-2 (1건)
  • nil 배열 크기 다양: 1~8개 — bulk 작업의 실패 항목 수에 따라 변동
  • 2026-04-03에 11건 집중 발생
  • Worker class는 항상 BulkSavePartialJsonToFileWorker#perform
  • 모든 경우 exponential backoff 패턴 동일 (0.1s, 0.2s, 0.4s, 0.8s, 1.6s, 3.2s)

Fix Recommendation#

즉시 조치 (Critical)#

TSLA-12264 커밋을 master 브랜치에 머지하여 프로덕션에 배포해야 합니다.

이 커밋은 이미 두 곳에서 수정을 포함합니다:

  • app/repositories/concerns/bulkable_repository.rb:71.compact 추가하여 nil ID 제거
  • app/workers/bulk_save_partial_json_to_file_worker.rb:7-15 — nil guard 추가로 빈 ID 배열 조기 반환

수정 코드가 이미 develop에 존재하므로 새로운 코드 작성이 필요 없으며, release 프로세스를 통해 master에 반영하면 됩니다.

단기 개선 (1주 이내)#

  • BulkableRepository#bulk!에서 모든 항목이 실패한 경우 worker를 아예 호출하지 않는 조건 추가를 검토해야 합니다 (모든 ID가 nil이면 _model_ids.compact는 빈 배열이므로 worker 호출이 불필요)
  • BulkableFactory#bulk!_new_model_ids 처리도 동일한 패턴인지 확인 필요 — line 66에서 nil 삽입 후 line 63의 .compact가 호출되지만, 순서상 .compact가 nil 삽입 이전에 실행되므로 Factory 경로는 영향받지 않음

장기 개선 (재발 방지)#

  • developmaster 머지 프로세스를 점검하여, 버그 수정 커밋이 적시에 프로덕션에 반영되도록 release 파이프라인을 개선해야 합니다
  • BulkSavePartialJsonToFileWorker에 입력 검증을 강화하여 nil ID가 전달되면 조기에 감지하고 로깅하는 방어적 코드 추가 검토

Monitoring#

  • 다음 Datadog 쿼리로 수정 배포 후 에러 재발 여부를 모니터링:
text
service:cupixworks-worker status:error "does not match after" @environment:production
  • BulkSavePartialJsonToFileWorker의 nil ID 감지를 위한 경고 모니터 설정:
text
service:cupixworks-worker "no valid IDs provided" @environment:production

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial — 이미 작성된 수정(TSLA-12264)을 master에 머지하고 배포하면 해결됩니다. 에러 자체는 data warehouse의 partial JSON 파일 저장 실패이므로 사용자 기능에 직접적 영향은 없으나, 약 6.3초간 불필요한 sleep으로 worker 리소스가 낭비됩니다.