HTTP request failed
RCA: HTTP request failed (GET /api/v1/panos returning 502/504)
Overview#
What Happened#
2026-07-09 09:37~10:03 KST 사이 약 26분 동안, cupixworks-sitetrack-preprocessor-agent가 tesla API의 GET /api/v1/panos?capture_id=...&page=...&per_page=100 호출에서 nginx 502/504 응답을 받고 HttpError: HTTP request failed로 실패했다. us-west-2 리전에서 3건이 발생했으며 서로 다른 3개 세션·3개 sitetrack job이 영향을 받았다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | HttpError |
| exception.message | HTTP request failed |
| top_frame | panoApi.js:2132 (tesla typescript-node-sdk) |
| runtime | Node.js, @tesla/typescript-node-sdk@1.13.3-SNAPSHOT.202605200456, request@2.88.2 |
| deploy | agent bundle at /tmp/agent/dist |
| env | production, us-west-2 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| clark-vdc (team.id 87) | 2 | sitetrack 20907, 20944 preprocessor job 실패 |
| gilbaneco (team.id 780) | 1 | sitetrack 20921 preprocessor job 실패 |
Timeline#
- 2026-07-09 09:37 KST — 최초 발생. sitetrack 20921 (gilbaneco, capture_id=722123 page=2)와 sitetrack 20907 (clark-vdc, capture_id=730827 page=3)이 동시에 nginx 504로 실패.
- 2026-07-09 10:03 KST — sitetrack 20944 (clark-vdc, capture_id=730681 page=1)가 nginx 502로 실패. 이후 동일 클러스터 발생 없음.
- 2026-07-09 10:04 KST — 동일 시간대에
cupixworks-api의Api::V1::PanosController#index요청은200응답을 정상적으로 반환(Datadog 로그로 확인) → API/Rails 자체 장애는 아님.
Error Log#
HTTP request failed
전체 stack 및 request 컨텍스트:
HttpError: HTTP request failed
at Request._callback (/tmp/agent/dist/node_modules/.pnpm/@tesla+typescript-node-sdk@1.13.3-SNAPSHOT.202605200456_219227d3097982d5f6233a3b9812920d/node_modules/@tesla/typescript-node-sdk/api/panoApi.js:2132:40)
at self.callback (/tmp/agent/dist/node_modules/.pnpm/request@2.88.2/node_modules/request/request.js:185:22)
at Request.emit (node:events:524:28)
...
Impact#
- Service:
cupixworks-sitetrack-preprocessor-agent - Team: gilbaneco (그리고 clark-vdc)
- 발생 횟수: 3
- 최초 발생: 2026-07-09 09:37 KST
- 최근 발생: 2026-07-09 10:03 KST
- 영향: sitetrack preprocessor 작업 3건이 즉시 실패. Retry 로직이 없어 해당 job은 error handling에 따라 실패 종료 또는 상위 큐에서 재시도되어야 함.
SiteTrackPreprocessorRunner::createTargetModels파이프라인은 첫 실패에서 중단되므로 이후 단계(elements, tasks, textures, element_records 로딩)는 실행되지 않는다.
Root Cause Summary#
Agent가 tesla API의 GET /api/v1/panos 를 페이지네이션으로 호출하는 중 upstream(nginx / ALB) 에서 502 Bad Gateway 및 504 Gateway Time-out HTML 응답을 반환했다. tesla API(cupixworks-api) 자체는 같은 시간대에 정상적으로 /api/v1/panos 요청을 200으로 처리하고 있었으므로 Rails 애플리케이션 장애가 아닌, API 프론트의 nginx가 개별 요청에 대해 백엔드 응답을 받지 못했거나(bad gateway) 응답 대기 중 타임아웃(gateway timeout) 이 발생한 upstream 인프라 이슈이다. Agent 측에서는 paginateAll 헬퍼가 각 페이지 호출을 retry 없이 그대로 upstream 예외로 전파하며, HttpError가 나오는 즉시 preprocessor 파이프라인 전체가 중단된다.
Technical Analysis#
Code Path#
Entry point: packages/cupix-sitetrack-preprocessor-agent/src/runner/sitetrack-preprocessor-runner.ts:14 (createCPCaptures 호출).
SiteTrackPreprocessorRunner.createTargetModels→createCPCaptures(cpSitetrack)BaseSiteinsightsPreprocessorRunner.createCPCaptures가 각 capture 마다pano.getAll(cpCapture.id)호출
@trace()
async createCPCaptures(cpParent: CPSitetrack): Promise<CPCapture[]> {
logger.debug('BasePreprocessorRunner::createCPCaptures | begin');
const cpCaptures = await createModels(CPCapture, cpParent, this.getSrvCaptures(cpParent));
for (const cpCapture of cpCaptures) {
logger.debug('BasePreprocessorRunner::createCPCaptures | id: %d, ...', cpCapture.id, ...);
const cpPanos = await createModels(CPPano, cpCapture, this.cupixApi.pano.getAll(cpCapture.id));
const cpVideos = await createModels(CPVideo, cpCapture, this.cupixApi.video.getAll(cpCapture.id).then(videos => videos.filter(v => v.state === 'done')));
logger.debug('BasePreprocessorRunner::createCPCapture | end - id: %d, panos_count: %d, videos_count: %d', cpCapture.id, cpPanos.length, cpVideos.length);
}
...
}
PanoApiModule.getAll이paginateAll을 통해 페이지별로PanoApi.getPanos호출:
getAll = async (captureId: number): Promise<Array<TESLA.Pano>> => {
const api = await this.api();
return paginateAll<TESLA.Pano>(async (page, perPage) => {
const res = await api.getPanos(
Fields.PanoFields,
captureId,
undefined,
undefined,
undefined,
page,
perPage
);
return unwrapPaginatedResponse(res);
});
};
paginateAll은 각 페이지 응답을 단순히 await 하며, 에러 처리·재시도 로직이 전혀 없다:
export const paginateAll = async <T>(
fetchPage: (page: number, perPage: number) => Promise<PaginatedResponse<T>>,
perPage = 100
): Promise<T[]> => {
const results: T[] = [];
let page = 1;
while (true) {
const list = await fetchPage(page, perPage);
...
const next = list?.pagination?.next_page;
if (next == null) break;
page = next;
}
return results;
};
Failure point: @tesla/typescript-node-sdk/api/panoApi.js:2132 — SDK 내부 request 콜백에서 non-2xx 응답을 받으면 HttpError('HTTP request failed') 를 던진다.
기대 동작: nginx 5xx 같은 일시적 upstream 오류는 짧은 backoff 후 재시도되어 preprocessor 파이프라인이 완주해야 한다.
실제 동작: 첫 번째 5xx 응답에서 HttpError 가 그대로 상위로 전파되어 createTargetModels 가 중단되고 sitetrack job 이 실패한다.
Log Evidence#
Datadog 재현 쿼리:
service:cupixworks-sitetrack-preprocessor-agent status:error @environment:production "HTTP request failed"
원 로그 레코드 3건의 핵심 필드:
{
"@timestamp": "2026-07-09T00:37:34.332Z",
"team": { "domain": "gilbaneco", "id": 780 },
"sitetrack": { "id": 20921 },
"job": { "id": 1183027 },
"response": {
"statusCode": 504,
"headers": { "server": "nginx", "content-type": "text/html" },
"body": "<html>\r\n<head><title>504 Gateway Time-out</title></head>...",
"request": {
"method": "GET",
"uri": {
"hostname": "api-tesla.cupix.internal",
"pathname": "/api/v1/panos",
"query": "...capture_id=722123&page=2&per_page=100"
}
}
}
}
{
"@timestamp": "2026-07-09T00:37:34.333Z",
"team": { "domain": "clark-vdc", "id": 87 },
"sitetrack": { "id": 20907 },
"response": {
"statusCode": 504,
"body": "<html>...504 Gateway Time-out...",
"request": { "uri": { "query": "...capture_id=730827&page=3&per_page=100" } }
}
}
{
"@timestamp": "2026-07-09T01:03:13.775Z",
"team": { "domain": "clark-vdc", "id": 87 },
"sitetrack": { "id": 20944 },
"response": {
"statusCode": 502,
"body": "<html>...502 Bad Gateway...",
"request": { "uri": { "query": "...capture_id=730681&page=1&per_page=100" } }
}
}
Upstream(cupixworks-api) 정상 처리 확인 (같은 5분 창 내 200 응답 다수):
Datadog 쿼리:
service:cupixworks-api "Api::V1::PanosController#index"
결과 예시 (2026-07-09T00:38:33Z–00:38:59Z 사이 20개 로그가 모두 [200] GET /api/v1/panos (Api::V1::PanosController#index)):
[200] GET /api/v1/panos (Api::V1::PanosController#index) 2026-07-09T00:38:56.483Z
[200] GET /api/v1/panos (Api::V1::PanosController#index) 2026-07-09T00:38:56.483Z
[200] GET /api/v1/panos (Api::V1::PanosController#index) 2026-07-09T00:38:56.482Z
...
그리고 동일 시간 창(00:35~01:05Z) service:cupixworks-api status:error 는 0건, service:cupixworks-api (capture_id:730681 OR capture_id:730827 OR capture_id:722123) 는 0건 → 실패한 3개 요청은 Rails 애플리케이션에 도달하지 않았거나 로그 이전에 nginx 레벨에서 종료되었음을 시사한다. 이는 502/504 응답 특성(백엔드에 도달하지 못했거나 응답을 받기 전에 nginx가 포기)과 일치한다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | tesla API(Rails)의 PanosController#index 자체가 5xx 반환 (예: 쿼리 성능 저하, DB 락) |
Agent 로그에 5xx 있음 | 같은 시간대 Api::V1::PanosController#index 로그가 모두 [200], service:cupixworks-api status:error 0건, 실패 요청 capture_id 로 API 로그 매칭 0건 |
Rejected |
| H2 | nginx/ALB 레벨 upstream 오류 (백엔드 unreachable 또는 응답 지연) → 502/504 반환 | 응답 body가 nginx 기본 502/504 HTML, response.headers.server: "nginx", API 로그에 해당 요청 미도달, AWSALB 쿠키 존재 |
없음 (단, 원인 세분화 — ALB target 재기동/DB 커넥션 풀 소진/rolling deploy 등 — 는 인프라 로그가 필요) | Confirmed (upstream infra) |
| H3 | Agent 재시도 로직 부재로 일시적 오류가 job 실패로 확대 | packages/api/src/utils/tesla-api.ts:102-120 paginateAll 에 try/catch·retry 없음, packages/api/src/utils/ 전체에 retry 문자열 매치 0 |
없음 | Confirmed (aggravating factor) |
| H4 | 특정 tenant/team 문제 (권한/쿼터) | 하나의 team만 실패 시 지지 | 실제로 2개 team(clark-vdc, gilbaneco) 3개 sitetrack에 걸쳐 발생, 다른 tenant는 같은 시간에 200 성공 | Rejected |
| H5 | 응답 payload 크기 초과 / query string 초과 길이 | request query에 32개 fields 나열되어 URL이 길다 | 정상 시간대에는 동일한 fields 세트로 200 성공, URL 길이가 502/504 원인이면 지속 실패해야 하나 총 3건만 발생 | Rejected |
Fix Recommendation#
즉시 조치 (Critical)#
- 별도 코드 수정 없음. Upstream infra 이슈로 판단되며 26분 창에서 자연 해소됨. 인프라 팀에 2026-07-09 00:37~01:03Z us-west-2
api-tesla.cupix.internal앞단 nginx/ALB 로그와 target 상태를 확인하도록 요청. ALB target health, nginxproxy_read_timeout, backend Rails/Puma worker 사용률, deploy 이벤트를 교차 점검. - 실패한 sitetrack job 3건(20907, 20921, 20944)에 대해 상위 스케줄러/job manager 에서 자동 재시도되지 않았다면 수동 재실행.
단기 개선 (1주 이내)#
packages/api/src/utils/tesla-api.ts의paginateAll(102-120) 에 transient 5xx (502/503/504) 및 네트워크 에러에 한해 exponential backoff retry 를 추가하는 방향으로 개선. 재시도는 pagination 함수 내부에서 페이지 단위로 수행 (전체 페이지네이션을 처음부터 다시 돌지 않도록). 최대 23회, base 500ms1s 정도가 안전.- 또는 tesla SDK 호출을 감싸는 공통 헬퍼(
getApi계층) 에 axios-retry 유사 미들웨어를 얹는 방식을 검토. 이 경우 다른 endpoint (video, element, task 등) 도 함께 보호된다 — 참고:pano.api.ts,video.api.ts,element.api.ts등 모든 API 모듈이 동일한getApi/paginateAll패턴을 사용. - 로그 품질 개선: 현재 message가 단순
HTTP request failed라 클러스터링 시 컨텍스트가 손실된다.HttpError를 로깅할 때status,path,capture_id,page를 message에 포함하면 fingerprint 분리 및 원인 파악이 훨씬 빠르다 (splat만 붙이면 되므로 위험도 낮음).
장기 개선 (재발 방지)#
- Agent → tesla API 호출에 대해 circuit breaker + retry with jitter 를 표준화. 특히 사이트트랙 preprocessor처럼 다단계 API 호출로 이루어진 파이프라인에서는 임의의 한 페이지 실패로 job 전체가 무너지는 구조가 반복 이슈 원인이 된다.
- Rails/API 프론트의 nginx
proxy_read_timeout, ALB idle timeout, Puma worker 수,/api/v1/panosPanosController#index응답 시간 p95/p99를 대시보드로 노출하여 upstream 지연을 조기 탐지. /api/v1/panos?capture_id=X응답 사이즈·처리 시간 최적화 — 32개 fields 요청 payload를 리뷰하고 필요 최소로 축소할 수 있는지 확인 (agent-sideFields.PanoFields정의).
Monitoring#
- Agent 실패율 (per team):
sum:trace.http.request.errors{service:cupixworks-sitetrack-preprocessor-agent,env:production} by {team_domain}.as_rate()
- tesla API
PanosController#index5xx 및 지연:
sum:trace.rack.request.errors{service:cupixworks-api,resource_name:api/v1/panos#index,env:production}.as_rate()
avg:trace.rack.request.duration{service:cupixworks-api,resource_name:api/v1/panos#index,env:production}
- Agent HttpError 횟수 (본 클러스터 재발 감지):
logs("service:cupixworks-sitetrack-preprocessor-agent status:error \"HTTP request failed\"").index("*").rollup("count").last("15m")
Risk Assessment#
- Risk level: medium — 한 번의 26분 window에서 3건 발생. Job은 실패했지만 데이터 유실은 없고 재실행으로 복구 가능. 그러나 retry 로직 부재로 upstream 지연 발생 시 동일 실패 패턴이 재현될 수 있음.
- 예상 복잡도: standard —
paginateAll에 retry wrapping 추가는 로컬 변경이며 다른 agent 서비스가 동일 헬퍼를 사용하므로 회귀 리스크는 있으나 관리 가능. 인프라 원인 규명은 별도로 진행.