Failed to save Partial JSON Capture 701882 to file /var/data_changes/captures/partial/2026-05-22/usw
RCA: Failed to save Partial JSON Capture to file (No space left on device)
Overview#
What Happened#
2026-05-22 20:20 UTC부터 2026-05-23 00:38 UTC까지 약 4시간 동안, cupixworks-worker 서비스의 SavePartialJsonToFileWorker가 /var/data_changes/ 볼륨의 디스크 용량 부족(Errno::ENOSPC)으로 인해 partial JSON 파일 저장에 실패했다. Capture, ElementTrace, Pointcloud, Element 등 다수의 엔티티에서 총 8,272건의 에러가 발생했다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Errno::ENOSPC |
| exception.message | No space left on device @ rb_sysopen - /var/data_changes/captures/partial/2026-05-22/uswe2/...json.gz |
| top_frame | app/models/concerns/data_ware_house/partial_json.rb:144 |
| runtime | Ruby 3.3.0, Sidekiq 7.3.9 |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| Data Warehouse (data_changes pipeline) | 8,272 | Partial JSON export 실패 — downstream 데이터 동기화 지연 |
Timeline#
- 2026-05-22T20:20:48Z — 최초 에러 발생.
rb_sysopensyscall에서 ENOSPC 에러 시작 - 2026-05-22T20:20~00:36 — 4시간 동안 지속적 실패. Sidekiq가 새 job을 계속 받으며 에러 누적
- 2026-05-23T00:36+ — 날짜 변경으로 새 디렉토리(
/2026-05-23/) 생성 시도 시dir_s_mkdir에서도 ENOSPC 발생 - 2026-05-23T00:38:47Z — 관측 윈도우 내 마지막 에러
Error Log#
Failed to save Partial JSON Capture 701882 to file /var/data_changes/captures/partial/2026-05-22/uswe2/1779480277939315-captures-701882.json.gz
Impact#
- Service:
cupixworks-worker - 발생 횟수: 8,272
- 최초 발생: 2026-05-22T20:20:48.465Z
- 최근 발생: 2026-05-23T00:38:47.671Z
영향받은 엔티티: Capture (701289, 701550, 701882 등 다수), ElementTrace (102272260, 115321486 등 다수), Pointcloud (1103877~1103882), Element (11612623, 14936857 등). Partial JSON export 실패로 인해 downstream data lake 동기화가 4시간 이상 중단되었으며, worker의 retry: false 설정으로 인해 실패한 job은 자동 재처리되지 않아 데이터 유실 가능성이 있다.
Root Cause Summary#
/var/data_changes/ 파티션의 디스크 공간이 완전히 소진되어 File.open(filepath, 'wb') 호출 시 Errno::ENOSPC (No space left on device) 예외가 발생했다. 이 볼륨은 모든 partial JSON export 파일이 날짜별로 파티셔닝되어 저장되는 곳으로, 오래된 파일에 대한 자동 정리(cleanup/rotation) 메커니즘이 코드에 존재하지 않아 시간이 지남에 따라 용량이 고갈된 것으로 판단된다.
Technical Analysis#
Code Path#
- Entry point:
SavePartialJsonToFileWorker#perform(app/workers/save_partial_json_to_file_worker.rb:63) - Model method:
DataWareHouse::PartialJson#save_partial_json_to_file→generate_and_save_partial_json(partial_json.rb:115-118) - Failure point:
save_partial_json_file—File.open(filepath, 'wb')(partial_json.rb:144)
Worker가 Sidekiq data_changes 큐에서 job을 받아 모델의 partial JSON을 gzip 압축 후 /var/data_changes/ 경로에 저장한다:
begin
model.save_partial_json_to_file(all_data: all_data, operation: operation, changes: changes, timestamp: timestamp)
rescue NoMethodError, Errno::ENOSPC, Errno::EACCES => e
Cupix::Logger.error(e.message, class: self.class, function: 'perform', error_class: e.class.name)
end
실제 파일 저장 로직에서 디렉토리 생성 후 파일을 쓸 때 ENOSPC가 발생한다:
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)
# ... permission check ...
raise Errno::EACCES, "Directory #{dirname} is not writable ..."
end
File.open(filepath, 'wb') { |file| file.write(content) } # ← ENOSPC 발생 지점
rescue Errno::EACCES, Errno::ENOSPC => e
Cupix::Logger.error("Failed to save Partial JSON #{self.class.name} #{self.id} to file #{filepath}", ...)
end
파일 경로 구성:
def partial_json_object_key(datetime: nil, timestamp: nil)
pluralized_model_name = self.class.name.underscore.pluralize
partitioning_datetime = resolve_partitioning_datetime(datetime)
_timestamp = timestamp || current_timestamp
"#{pluralized_model_name}/partial/#{partitioning_datetime.strftime('%Y-%m-%d')}/#{region_code}/#{_timestamp}-#{pluralized_model_name}-#{self.id}.json.gz"
end
디렉토리 생성 시에도 동일한 ENOSPC가 발생 (자정 이후):
def create_directory_if_not_exists(filepath)
dirname = File.dirname(filepath)
FileUtils.mkdir_p(dirname) unless File.directory?(dirname)
end
기대 동작: gzip 압축된 JSON을 /var/data_changes/{model}/partial/{date}/{region}/ 경로에 저장하고 success 로그를 남긴다.
실제 동작: 디스크 용량 부족으로 File.open 또는 FileUtils.mkdir_p에서 Errno::ENOSPC가 발생하고, rescue 블록에서 에러 로그를 남긴 후 조용히 실패한다. retry: false 설정으로 job이 재시도되지 않아 해당 변경 데이터가 영구 유실된다.
Log Evidence#
Datadog 쿼리:
service:cupixworks-worker status:error "Failed to save Partial JSON" @environment:production
대표적인 에러 로그 (rb_sysopen 실패):
Failed to save Partial JSON Capture 701882 to file /var/data_changes/captures/partial/2026-05-22/uswe2/1779486119759576-captures-701882.json.gz
Error: No space left on device @ rb_sysopen - /var/data_changes/captures/partial/2026-05-22/uswe2/1779486119759576-captures-701882.json.gz
스택 트레이스:
/var/app/current/app/models/concerns/data_ware_house/partial_json.rb:197:in `initialize'
/var/app/current/app/models/concerns/data_ware_house/partial_json.rb:197:in `open'
/var/app/current/app/models/concerns/data_ware_house/partial_json.rb:197: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/save_partial_json_to_file_worker.rb:63:in `perform'
자정 이후 mkdir 실패:
Error: No space left on device @ dir_s_mkdir - /var/data_changes/pointclouds/partial/2026-05-23
/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'
영향받은 엔티티 유형과 에러 진행 패턴:
| 시간대 | 실패 syscall | 의미 |
|---|---|---|
| 2026-05-22T20:20+ | rb_sysopen |
파일 쓰기 불가 (데이터 블록 소진) |
| 2026-05-23T00:36+ | dir_s_mkdir |
디렉토리 생성도 불가 (inode 또는 전체 용량 소진) |
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | /var/data_changes/ 디스크 용량 소진 (ENOSPC) |
모든 에러 메시지에 "No space left on device" 명시. 4시간 동안 모든 엔티티 유형에서 동일 에러. 자정 이후 mkdir도 실패. | — | Confirmed |
| H2 | 파일 권한 문제 (EACCES) | 코드에 EACCES 처리 존재 (partial_json.rb:127-141) |
에러 메시지가 ENOSPC이지 EACCES가 아님. writable check를 통과한 후 write에서 실패. | Rejected |
| H3 | 특정 capture 701882의 데이터 이상 (거대 JSON) | — | 동일 시간대에 Capture, ElementTrace, Pointcloud, Element 등 다수 엔티티에서 동일 에러 발생. 단일 엔티티 문제가 아닌 시스템 전체 이슈. | Rejected |
| H4 | Sidekiq 과부하로 인한 일시적 write 실패 | 같은 시간대에 대량 job 실행 | ENOSPC는 부하와 무관한 OS-level 에러. 재시도해도 동일 결과. | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- 디스크 용량 확보:
/var/data_changes/볼륨에서 오래된 파일 정리 (예: 7일 이상 된 partial JSON 파일 삭제). 이미 S3로 sync된 파일은 로컬에서 제거 가능. - 유실 데이터 복구:
retry: false설정으로 실패한 8,272건의 job은 자동 재처리되지 않으므로, 영향받은 레코드들의 partial JSON을 수동으로 재생성해야 한다.
단기 개선 (1주 이내)#
- 자동 cleanup 스크립트 추가: cron job 또는 systemd timer로
/var/data_changes/내 N일 이상 된 파일을 자동 삭제. S3 sync 완료 확인 후 삭제하는 로직이 이상적. - 디스크 사용량 모니터링 알림:
/var/data_changes/파티션 사용률이 80%를 초과하면 알림을 발생시켜 사전 대응. - 실패 시 재시도 전략 검토:
retry: false대신 제한적 retry (예: 3회)와 disk space 부족 시 별도 dead letter queue로 이동하는 방안 고려.
장기 개선 (재발 방지)#
- EBS 볼륨 자동 확장 (Auto Scaling) 또는 충분한 고정 용량 할당으로 디스크 고갈 자체를 방지.
- Streaming upload: 로컬 디스크에 임시 저장하지 않고 S3에 직접 streaming upload하여 로컬 디스크 의존도 제거.
- Circuit breaker 패턴: ENOSPC 발생 시 일정 기간 새 write 시도를 중단하고, 디스크 복구 후 backlog을 처리하는 방식으로 불필요한 에러 로그 폭증 방지.
Monitoring#
/var/data_changes/파티션 디스크 사용량 메트릭 (80% threshold alert)- Partial JSON save 실패율 모니터
service:cupixworks-worker status:error "Failed to save Partial JSON"
system.disk.in_use{device:/var/data_changes} > 0.8
Risk Assessment#
- Risk level: medium
- 예상 복잡도: standard (디스크 정리는 trivial이나, 유실 데이터 복구 및 자동 cleanup 구축이 필요)