ES /docs

Failed to save partial JSON ElementTrace 102272260

RCA: Failed to save partial JSON ElementTrace 102272260

Overview#

What Happened#

2026-05-22 20:20 UTC부터 약 4시간 동안 us-west-2 리전의 cupixworks-worker 호스트 ip-10-1-18-233에서 디스크 공간 부족(No space left on device)으로 인해 partial JSON 파일 저장이 전면 실패했다. ElementTrace를 포함한 4개 모델(ElementTrace, Pointcloud, Capture, Editing)에서 총 719,410건의 에러가 발생했으며, 데이터 웨어하우스로의 변경 데이터 전파가 완전히 중단되었다.

Quick Facts#

Field Value
exception.class Errno::ENOSPC
exception.message No space left on device @ rb_sysopen - /var/data_changes/element_traces/partial/2026-05-22/uswe2/{timestamp}-element_traces-{id}.json.gz
top_frame app/models/concerns/data_ware_house/partial_json.rb:144
runtime Ruby 3.3.0, Rails 7.2.2, Sidekiq 7.3.9
deploy production-us-west-2-20260519T0920Z0-3e770a15-cupixworks
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
Data Warehouse (ElementTrace) ~719,000 변경 데이터 파일 미생성 → downstream 동기화 누락
Data Warehouse (Pointcloud) 수백 건 Pointcloud 변경 이력 누락
Data Warehouse (Capture/Editing) 수십 건 Capture/Editing 변경 이력 누락

Timeline#

  1. 2026-05-22 20:20:42 UTC — 최초 에러 발생 (ElementTrace 102272260, host ip-10-1-18-233)
  2. 2026-05-22 20:59:52 UTC — Pointcloud 모델에서도 동일 에러 확인
  3. 2026-05-22 23:57:59 UTC — 클러스터 내 마지막 에러 기록
  4. 2026-05-23 00:29:51 UTC — 날짜 변경 후 디렉토리 생성조차 실패 (dir_s_mkdir 에러)
  5. 2026-05-26 — Error Sweeper에 의한 RCA 수행

Error Log#

Datadog Logs

text
Failed to save partial JSON ElementTrace 102272260

Impact#

  • Service: cupixworks-worker
  • 발생 횟수: 719,410
  • 최초 발생: 2026-05-22T20:20:42.030Z
  • 최근 발생: 2026-05-22T23:57:59.494Z

Root Cause Summary#

호스트 ip-10-1-18-233.us-west-2.compute.internal/var/data_changes/ 파티션이 완전히 소진되어 Errno::ENOSPC (No space left on device) 예외가 발생했다. Sidekiq worker들이 partial JSON 파일을 디스크에 기록하려 할 때 파일 오픈(rb_sysopen) 또는 디렉토리 생성(dir_s_mkdir)이 불가능해졌고, 해당 에러가 rescue 블록에서 로깅만 되고 정상 리턴되면서 719,410건의 데이터 변경 이력이 소실되었다. 디스크 공간 관리(cleanup/rotation)가 부재하거나 데이터 축적 속도가 정리 속도를 초과한 것이 근본 원인이다.

Technical Analysis#

Code Path#

  • Entry point: BulkSavePartialJsonToFileWorker#perform (app/workers/bulk_save_partial_json_to_file_worker.rb:71)
  • Iteration: 배치 단위(50개)로 모델을 순회하며 각각 save_partial_json_to_file 호출
  • Core save: partial_json.rb:115generate_and_save_partial_json
  • Failure point: partial_json.rb:144File.open(filepath, 'wb') 에서 Errno::ENOSPC 발생

Worker에서 배치 처리:

app/workers/bulk_save_partial_json_to_file_worker.rb:71-75ruby
models.find_in_batches(batch_size: 50) do |groups|
  groups.each do |model|
    model.save_partial_json_to_file(all_data: true, operation: operation, timestamp: timestamp)
  end
end

Partial JSON 파일 저장 메서드:

app/models/concerns/data_ware_house/partial_json.rb:121-149ruby
def save_partial_json_file(content, path)
  filepath = Rails.env.development? ? "./tmp/storage/#{path}" : "/var/data_changes/#{path}"
  create_directory_if_not_exists(filepath)

  # Verify directory is writable before attempting file write
  dirname = File.dirname(filepath)
  unless File.writable?(dirname)
    # ... writability check and EACCES raise ...
  end

  File.open(filepath, 'wb') { |file| file.write(content) }
rescue Errno::EACCES, Errno::ENOSPC => e
  Cupix::Logger.error("Failed to save Partial JSON #{self.class.name} #{self.id} to file #{filepath}",
    class: self.class.name, module: 'DataWareHouse', function: 'save_partial_json_file',
    error: e.message, backtrace: e.backtrace)
end

기대 동작: File.open(filepath, 'wb')로 gzip 압축된 JSON 데이터를 /var/data_changes/ 하위에 저장하고, downstream 시스템이 이를 읽어 데이터 웨어하우스에 동기화.

실제 동작: 디스크 공간 부족으로 File.openErrno::ENOSPC를 발생시키고, rescue 블록에서 에러를 로깅한 뒤 정상 리턴. Worker 자체는 실패하지 않으나(retry: false) 데이터 파일이 생성되지 않아 변경 이력이 소실됨.

Bulk 호출 경로 (Repository):

app/repositories/concerns/bulkable_repository.rb:71,102-103ruby
bulk_save_changes_to_partial_json(_model_ids.compact)

def bulk_save_changes_to_partial_json(model_ids = [])
  BulkSavePartialJsonToFileWorker.perform_async(self.class.current_class.name, model_ids)
end

Log Evidence#

사용한 Datadog 쿼리:

text
service:cupixworks-worker status:error @environment:production "Failed to save partial JSON ElementTrace"
text
service:cupixworks-worker status:error @environment:production "Failed to save Partial JSON" "No space left on device"

대표 에러 로그 (ElementTrace 102272260 — 최초 발생):

json
{
  "message": "Failed to save partial JSON ElementTrace 102272260",
  "error": "No space left on device @ rb_sysopen - /var/data_changes/element_traces/partial/2026-05-22/uswe2/1779481227864174-element_traces-102272260.json.gz",
  "host": "ip-10-1-18-233.us-west-2.compute.internal",
  "pid": 3114827,
  "function": "bulk_partial_save_to_file",
  "class": "ElementTrace",
  "module": "DataWareHouse",
  "timestamp": "2026-05-22T20:20:42.030Z"
}

날짜 변경 후 디렉토리 생성 실패 (2026-05-23):

text
No space left on device @ dir_s_mkdir - /var/data_changes/element_traces/partial/2026-05-23

Stack trace:

text
/usr/lib64/ruby/3.3.0/fileutils.rb:402:in `mkdir'
/var/app/current/app/models/concerns/data_ware_house/base.rb:28:in `create_directory_if_not_exists'
/var/app/current/app/models/concerns/data_ware_house/partial_json.rb:176:in `save_partial_json_file'
/var/app/current/app/models/concerns/data_ware_house/partial_json.rb:171:in `generate_and_save_partial_json'
/var/app/current/app/models/concerns/data_ware_house/partial_json.rb:71:in `save_partial_json_to_file'
/var/app/current/app/workers/bulk_save_partial_json_to_file_worker.rb:73:in `block (2 levels) in perform'

Pointcloud 에러 (20:59 UTC):

json
{
  "message": "Failed to save Partial JSON Pointcloud 1103638 to file /var/data_changes/pointclouds/partial/2026-05-22/uswe2/1779483593397961-pointclouds-1103638.json.gz",
  "error": "No space left on device @ rb_sysopen - /var/data_changes/pointclouds/partial/2026-05-22/uswe2/1779483593397961-pointclouds-1103638.json.gz",
  "host": "ip-10-1-18-233.us-west-2.compute.internal",
  "function": "save_partial_json_file",
  "class": "Pointcloud",
  "timestamp": "2026-05-22T20:59:54.252Z"
}

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 /var/data_changes/ 파티션 디스크 공간 소진 모든 에러가 Errno::ENOSPC ("No space left on device")이며, 단일 호스트 ip-10-1-18-233에서만 발생. rb_sysopendir_s_mkdir 모두 동일 원인. 4개 모델 클래스 전부 동일 에러. Confirmed
H2 파일 권한(Permission) 문제 코드에 Errno::EACCES 처리 존재, 이전 TSLA-12288에서 권한 문제 수정 이력 있음 로그 메시지가 모두 ENOSPC이며 EACCES 에러는 없음. 권한 체크 통과 후 파일 오픈에서 실패. Rejected
H3 특정 ElementTrace ID의 데이터 이상으로 인한 쓰기 실패 ElementTrace 102272260이 최초 에러 Pointcloud, Capture, Editing 등 모든 모델에서 동일 에러 발생. ID와 무관하게 모든 쓰기 실패. Rejected
H4 Sidekiq worker 버그 (batch 처리 로직 오류) Worker가 retry: false로 재시도하지 않음 Worker 코드 자체는 정상 동작. rescue 블록이 ENOSPC를 잡아 로깅하는 것은 의도된 동작. 문제는 인프라(디스크)에 있음. Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 호스트 ip-10-1-18-233/var/data_changes/ 디스크 사용량 확인 및 오래된 partial JSON 파일 정리
  • 디스크 공간 확보 후 에러가 자연 해소되었는지 확인
  • 에러 발생 기간(4시간) 동안 누락된 변경 이력에 대한 full sync 수행 필요 여부 검토

단기 개선 (1주 이내)#

  • /var/data_changes/ 디렉토리에 대한 자동 정리(cleanup) cronjob 추가: 일정 기간(예: 7일) 이상 된 partial JSON 파일 삭제
  • 디스크 사용률 임계값(80%, 90%) 알림 설정으로 사전 감지
  • save_partial_json_file에서 ENOSPC 발생 시 circuit breaker 또는 rate limiting 도입 검토: 현재는 모든 건마다 개별 에러 로그를 남겨 719K건의 중복 로그 발생

장기 개선 (재발 방지)#

  • Partial JSON 파일을 로컬 디스크 대신 S3에 직접 스트리밍하는 방식으로 아키텍처 변경 검토 (디스크 의존도 제거)
  • 또는 EBS 볼륨 자동 확장(autoscaling) 설정
  • 디스크 풀 상태에서의 데이터 소실 방지를 위한 재처리 메커니즘 도입: 현재 retry: false이므로 ENOSPC로 실패한 작업은 영구 소실됨

Monitoring#

  • /var/data_changes/ 파티션 디스크 사용률 모니터링 (80% 경고, 90% 긴급)
  • ENOSPC 에러 발생 빈도 알림 (1분 내 10건 이상 시 즉시 알림)
text
service:cupixworks-worker status:error "No space left on device" @environment:production
text
system.disk.in_use{host:ip-10-1-18-233.us-west-2.compute.internal,device:/var/data_changes} > 0.9

Risk Assessment#

  • Risk level: high
  • 예상 복잡도: standard (디스크 정리는 즉시 가능, 구조적 개선은 아키텍처 변경 필요)