Elasticsearch::Transport::Transport::Errors::NotFound: [404] {"error":{"root_cause":[{"type":"document_missing_exception
RCA: Elasticsearch::Transport::Transport::Errors::NotFound [404] document_missing_exception
Overview#
What Happened#
tesla (cupixworks-api / cupixworks-worker)의 Searchable concern이 레코드 update 시점에 Elasticsearch _update 요청을 보내는데, 대상 문서가 아직 색인되지 않은 상태라 ES가 [404] document_missing_exception을 반환한다. 이 실패한 client.update HTTP 요청이 cupixworks-elasticsearch APM instrumentation span에 error로 태깅되어 Error Tracking 이슈로 집계된다. 다만 애플리케이션 코드는 이 예외를 명시적으로 rescue 하여 warn 로그를 남기고 _index_document로 문서를 재색인하므로, 사용자에게 노출되는 실패나 데이터 유실은 없다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Elasticsearch::Transport::Transport::Errors::NotFound |
| exception.message | [404] {"error":{"root_cause":[{"type":"document_missing_exception", ...}]}} |
| top_frame | app/models/concerns/searchable.rb:94 (client.update) |
| runtime | Ruby / Rails (elasticsearch-transport 7.5.0, faraday 0.17.5 + patron) |
| env | production |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| tesla (search indexing) | 18281 (16개월 누적) | 없음 — rescue 후 자동 재색인, 사용자 무영향 |
Timeline#
- 2023-09-12 18:27 KST — 최초 발생 (Representative sample:
editing_entitiesindex,_doc][23575]). - 2026-08-03 21:07 KST — 최근 발생 (
Cupix::Cron::Integration, indexcupix-cron-integrations). - 2026-08-04 14:08 KST — last_seen (
Workarea, indexworkareas,_doc][139962]). - 2026-08-04 — RCA 수행. Representative가 STALE임을 확인 (현재는
editing_entities가 아닌elements/workareas/cupix-cron-integrations등 다양한 index에서 발생).
Error Log#
[404] {"error":{"root_cause":[{"type":"document_missing_exception","reason":"[_doc][23575]: document missing","index_uuid":"OrsiP2DkQzWgpo7vU2vU5g","shard":"4","index":"editing_entities"}],"type":"document_missing_exception","reason":"[_doc][23575]: docum
Impact#
- Service:
cupixworks-elasticsearch(APM instrumentation span — 실제 앱은 tesla) - 발생 횟수: 18281 (first_seen 2023-09-12 이후 약 16개월 누적)
- 최초 발생: 2023-09-12 18:27 KST
- 최근 발생: 2026-08-04 14:08 KST
Root Cause Summary#
Searchable concern은 레코드 생성 시 after_commit on: [:create] → _index_document로 ES에 색인하고, 갱신 시 after_commit on: [:update] → _update_document로 부분 갱신(client.update)을 수행한다. 색인이 아직 완료되지 않았거나(색인 실패 후 BulkIndexWorker 재색인 대기 중, dual-write tmp_index 미완, ES refresh 지연 등) 문서가 존재하지 않는 상태에서 update가 먼저 도달하면 ES는 존재하지 않는 문서에 대한 partial update를 거부하고 [404] document_missing_exception을 반환한다. 이 실패한 HTTP 요청 자체가 cupixworks-elasticsearch APM span에 error로 태깅되어 Error Tracking에 집계되지만, searchable.rb:101-105가 이 NotFound 예외를 명시적으로 rescue → warn 로그 → _index_document 재색인으로 자가 치유한다. 즉 코드 결함이 아니라, ES update-before-index 순서 경합에 대한 정상적 fallback 경로이며 사용자 영향과 데이터 유실이 없다.
Technical Analysis#
Code Path#
- Entry point:
app/models/concerns/searchable.rb:16-18— 레코드 update 후after_commit콜백 _update_document→client.update호출 (searchable.rb:94) = Failure point (여기서 ES가 404 반환)- rescue → 재색인 (
searchable.rb:101-105)
update 콜백 등록:
after_commit on: [:create] do
_index_document
end
after_commit on: [:update] do
_update_document
end
update 요청과 404 rescue:
begin
request = {
id: __elasticsearch__.id,
body: { doc: attributes },
retry_on_conflict: 5
}
request.merge!(type: __elasticsearch__.document_type) if __elasticsearch__.document_type
results = __elasticsearch__.client.update(request.merge({ index: __elasticsearch__.index_name }))
Cupix::Logger.debug(results.to_json, class: self.class.name, function: __method__)
# NOTE: dual write to tmp_index while reindexing
if (tmp_index = self.class.fetch_tmp_index_name)
__elasticsearch__.client.update(request.merge(index: tmp_index))
end
rescue Elasticsearch::Transport::Transport::Errors::NotFound => e
Cupix::Logger.warn("NotFound - #{e.message}", class: self.class.name, function: __method__)
_index_document
end
기대 동작: update 대상 문서는 create 콜백에서 이미 색인되어 있어야 한다.
실제 동작: 문서가 아직 색인되지 않은 상태에서 update가 도달 → ES _update API가 partial update를 거부하고 document_missing_exception (404) 반환. client.update의 HTTP 요청 span이 error로 태깅됨. 하지만 즉시 rescue되어 _index_document(전체 색인)로 문서가 생성되므로 최종 상태는 정합적. _index_document 자체도 실패 시 BulkIndexWorker.perform_async로 비동기 재색인 큐잉(searchable.rb:50-53)되어 이중 안전망이 존재한다.
document_missing_exception이 왜 NotFound로 rescue되는지: elasticsearch-transport 7.5.0에서 HTTP 404 응답은 Elasticsearch::Transport::Transport::Errors::NotFound로 매핑된다. 따라서 rescue Elasticsearch::Transport::Transport::Errors::NotFound(searchable.rb:101)가 이 예외를 포착한다.
Log Evidence#
Error-level 검색은 0건 (완전히 rescue되어 error로 로깅되지 않음):
service:cupixworks-api "document_missing_exception" status:error → 0 logs (now-14d)
APM span 검색도 0건 (rescue된 예외):
service:cupixworks-elasticsearch "document_missing_exception" → 0 logs (now-14d)
실제 현재 발생은 warn 레벨의 _update_document rescue 로그로 확인됨. Representative가 지목한 editing_entities가 아니라 다양한 index에서 발생 (Representative STALE 증거):
service:cupixworks-api "NotFound - [404]" (now-14d)
{
"timestamp": "2026-08-04 14:08:21",
"status": "warn",
"message": "NotFound - [404] {\"error\":{\"root_cause\":[{\"type\":\"document_missing_exception\",\"reason\":\"[_doc][139962]: document missing\",\"index_uuid\":\"bSC11WWjRPSm9k0Hcjlr8Q\",\"shard\":\"0\",\"index\":\"workareas\"}], ...,\"status\":404}",
"class": "Workarea",
"function": "_update_document"
}
{
"timestamp": "2026-08-04 07:34:14",
"status": "warn",
"message": "NotFound - [404] {\"error\":{\"root_cause\":[{\"type\":\"document_missing_exception\",\"reason\":\"[_doc][3143880]: document missing\",\"index_uuid\":\"vLtrRdPyTd2p2xQFrCpDlw\",\"shard\":\"3\",\"index\":\"elements\"}], ...,\"status\":404}",
"class": "Element",
"function": "_update_document"
}
worker 측(cupixworks-worker)에서도 동일 패턴:
{
"timestamp": "2026-08-04 01:07:21",
"status": "warn",
"message": "NotFound - [404] {\"error\":{\"root_cause\":[{\"type\":\"document_missing_exception\",\"reason\":\"[_doc][6940]: document missing\",\"index_uuid\":\"hU4UOnmzT420WlJJ403dyg\",\"shard\":\"0\",\"index\":\"cupix-cron-integrations\"}], ...,\"status\":404}",
"class": "Cupix::Cron::Integration",
"function": "_update_document"
}
24h 샘플 분포: Element 49건, Workarea 1건 (+ worker의 Cupix::Cron::Integration). 각기 다른 record ID와 다른 index에 걸쳐 산발적으로 발생 — 특정 레코드 재진입이 아니라 create/update 순서 경합의 광범위한 저빈도 패턴.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | update-before-index 순서 경합 → 미색인 문서에 partial update 시도 → 404, 코드가 rescue 후 재색인 (noise) | searchable.rb:16-18 create/update 콜백 분리; :101-105 NotFound rescue → _index_document; 현재 로그 전량 status:warn _update_document |
— | Confirmed |
| H2 | Representative의 editing_entities 특정 index/문서 문제 (스키마·매핑 오류 등) |
Representative message가 editing_entities, _doc][23575] 지목 |
현재(last_seen) 로그는 workareas/elements/cupix-cron-integrations 등 다수 index. editing_entities는 현재 미발생. ET가 document_missing_exception 변형을 하나로 묶어 오래된 sample을 pin (STALE) |
Rejected |
| H3 | ES 클러스터 outage로 인한 update 실패 | status-board에 2026-07-29 dep:elasticsearch resolved 인시던트 존재 | last_seen(2026-08-04)는 그 이후; outage는 outage timeout/503으로 나타나며 document_missing_exception(문서 부재)와 무관. active 인시던트 없음 |
Rejected |
| H4 | 미처리 예외로 사용자 요청 500 실패 | — | status:error 0건; 전량 warn + rescue; NotFound < Elasticsearch::Transport::Transport::Error이므로 :115 상위 rescue에도 절대 도달 안 함 (:101이 먼저 포착) |
Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- 없음. 코드 결함이 아니며 사용자 영향/데이터 유실 없음.
searchable.rb:101-105의 rescue →_index_document경로가 이미 자가 치유를 수행한다.
단기 개선 (1주 이내)#
- Error Tracking 이슈 mute/ignore 처리 권장: 이 이슈는 정상 fallback 경로가 남기는 APM span error이므로 알람·트래킹 노이즈다. Error Tracking에서 이 이슈(
97df4ae0-514e-11ee-9822-da7ad0900002)를 ignore 처리. - (선택) APM span error 태깅 억제:
cupixworks-elasticsearchinstrumentation이 404 응답을 error로 태깅하지 않도록 datadog trace 설정에서document_missing_exception을 error 상태에서 제외하는 것을 검토. 단, ES span 전반의 404를 무시하면 실제 404 문제를 놓칠 수 있으므로 신중히.
장기 개선 (재발 방지)#
- update-before-index 경합 축소: create 색인과 update 부분 갱신이 별도
after_commit으로 분리되어 있어, 색인 지연(비동기 재색인 대기, tmp_index dual-write 진행, ES refresh 지연) 중 update가 앞서면 이 패턴이 재발한다. 근본적으로 update 경로가 문서 부재 시 곧바로 full index로 전환하는 현재 설계가 합리적이므로, 빈도가 낮다면 유지해도 무방. 빈도가 유의미하게 오르면 색인 파이프라인을 단일 큐(BulkIndexWorker) 기반으로 직렬화하는 방안을 검토.
Monitoring#
_update_document NotFound fallback 발생 추이 (warn 로그 기반). 이 값이 갑자기 급증하면 색인 파이프라인 지연을 의심:
service:cupixworks-api "NotFound - [404]" @function:_update_document
실제 미처리 실패(회귀) 감지 — 정상 상태에서는 0이어야 함:
service:cupixworks-api "ElasticsearchError" @function:_update_document status:error
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (코드 변경 없음, ET ignore 처리만 권장)
Noise Verdict#
noise — document_missing_exception(404)은 update-before-index 순서 경합으로 발생하지만 searchable.rb:101-105가 즉시 rescue 후 _index_document로 재색인해 자가 치유하므로 사용자 영향·데이터 유실이 없는 정상 fallback 경로다.