ES /docs

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#

  1. 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로 실패.
  2. 2026-07-09 10:03 KST — sitetrack 20944 (clark-vdc, capture_id=730681 page=1)가 nginx 502로 실패. 이후 동일 클러스터 발생 없음.
  3. 2026-07-09 10:04 KST — 동일 시간대에 cupixworks-apiApi::V1::PanosController#index 요청은 200 응답을 정상적으로 반환(Datadog 로그로 확인) → API/Rails 자체 장애는 아님.

Error Log#

Datadog Logs

text
HTTP request failed

전체 stack 및 request 컨텍스트:

text
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 Gateway504 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 호출).

  1. SiteTrackPreprocessorRunner.createTargetModelscreateCPCaptures(cpSitetrack)
  2. BaseSiteinsightsPreprocessorRunner.createCPCaptures 가 각 capture 마다 pano.getAll(cpCapture.id) 호출
packages/siteinsights/src/runner/base-siteinsights-preprocessor-runner.ts:42-57typescript
@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);
    }
    ...
}
  1. PanoApiModule.getAllpaginateAll 을 통해 페이지별로 PanoApi.getPanos 호출:
packages/api/src/api/pano.api.ts:32-46typescript
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);
    });
};
  1. paginateAll 은 각 페이지 응답을 단순히 await 하며, 에러 처리·재시도 로직이 전혀 없다:
packages/api/src/utils/tesla-api.ts:102-120typescript
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 재현 쿼리:

text
service:cupixworks-sitetrack-preprocessor-agent status:error @environment:production "HTTP request failed"

원 로그 레코드 3건의 핵심 필드:

log 1 (sitetrack 20921, gilbaneco)json
{
  "@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"
      }
    }
  }
}
log 2 (sitetrack 20907, clark-vdc)json
{
  "@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" } }
  }
}
log 3 (sitetrack 20944, clark-vdc)json
{
  "@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 쿼리:

text
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)):

text
[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, nginx proxy_read_timeout, backend Rails/Puma worker 사용률, deploy 이벤트를 교차 점검.
  • 실패한 sitetrack job 3건(20907, 20921, 20944)에 대해 상위 스케줄러/job manager 에서 자동 재시도되지 않았다면 수동 재실행.

단기 개선 (1주 이내)#

  • packages/api/src/utils/tesla-api.tspaginateAll (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/panos PanosController#index 응답 시간 p95/p99를 대시보드로 노출하여 upstream 지연을 조기 탐지.
  • /api/v1/panos?capture_id=X 응답 사이즈·처리 시간 최적화 — 32개 fields 요청 payload를 리뷰하고 필요 최소로 축소할 수 있는지 확인 (agent-side Fields.PanoFields 정의).

Monitoring#

  • Agent 실패율 (per team):
text
sum:trace.http.request.errors{service:cupixworks-sitetrack-preprocessor-agent,env:production} by {team_domain}.as_rate()
  • tesla API PanosController#index 5xx 및 지연:
text
sum:trace.rack.request.errors{service:cupixworks-api,resource_name:api/v1/panos#index,env:production}.as_rate()
text
avg:trace.rack.request.duration{service:cupixworks-api,resource_name:api/v1/panos#index,env:production}
  • Agent HttpError 횟수 (본 클러스터 재발 감지):
text
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 지연 발생 시 동일 실패 패턴이 재현될 수 있음.
  • 예상 복잡도: standardpaginateAll 에 retry wrapping 추가는 로컬 변경이며 다른 agent 서비스가 동일 헬퍼를 사용하므로 회귀 리스크는 있으나 관리 가능. 인프라 원인 규명은 별도로 진행.