API Gateway — 30s integration timeout exceeded by Athena query latency
RCA: failed to get validation data - error: 504 Gateway Timeout
Overview#
What Happened#
2026-04-26 22:26~23:06 UTC (약 40분간) cupixworks-api 서비스의 Cupix::SiteinsightsService#_get_element_record_counts 메서드에서 외부 AWS API Gateway Siteinsights 서비스 호출이 504 Gateway Timeout으로 실패했다. 단일 사용자(yohan.kim@cupix.com)가 3개 facility에 대해 반복 요청하면서 총 38건의 에러가 발생했으며, 클라이언트에는 HTTP 502 Bad Gateway로 응답되었다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | RestClient::Exception (wrapped as Cupix::Errors::System) |
| exception.message | failed to get validation data - error: 504 Gateway Timeout |
| top_frame | app/services/cupix/siteinsights_service.rb:282 |
| error_code | SYS20000 → BG10005 (502) |
| env | production, us-west-2 |
| deploy | 20260426T0631Z0, 20260424T1951Z0, 20260424T2039Z0 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| admin (team_id: 133) | 38 | Siteinsights validation 기능 사용 불가 — element record 정합성 검증 실패 |
Timeline#
- 2026-04-26T22:26:38Z — 최초 에러 발생 (facility
umphr6) - 2026-04-26T23:06:41Z — 마지막 에러 발생 (40분간 38건)
- 2026-04-27 — Error Sweeper 감지, RCA 수행
Error Log#
failed to get validation data - error: 504 Gateway Timeout
Impact#
- Service:
cupixworks-api - 발생 횟수: 38
- 최초 발생: 2026-04-26T22:26:38.972Z
- 최근 발생: 2026-04-26T23:06:41.544Z
- 영향 사용자: 1명 (
yohan.kim@cupix.com, admin team) - 영향 facility:
umphr6(29건),3fq7tj(8건),10ofni(1건) - 영향 호스트: 3대 (us-west-2 인스턴스 전체)
Root Cause Summary#
Cupix::SiteinsightsService#_get_element_record_counts 메서드가 AWS API Gateway의 Siteinsights /element_records/validation 엔드포인트로 POST 요청을 보내는데, Lambda 함수 timeout(60초)과 API Gateway integration timeout(29초, 기본값)의 불일치로 504 Gateway Timeout이 반환되었다. ElementRecordProxyFunction Lambda는 template.yaml:651에서 Timeout: 60으로 설정되어 있으나, api.yaml:260-266의 API Gateway integration 정의에는 timeoutInMillis가 명시되지 않아 AWS 기본값 29초가 적용된다. Lambda가 29초 이상 처리에 소요되면 — Lambda 자체는 아직 60초 제한 내에서 실행 중이지만 — API Gateway가 먼저 504를 반환한다. 에러 로그의 request duration이 ~31.6초로 나타나는 점(DB time ~1.9초 + API Gateway 29초 timeout + 네트워크 오버헤드)이 이를 뒷받침한다. RestClient.post 호출에 명시적 timeout이 없고, retry 로직도 없어 모든 요청이 즉시 실패로 처리되었다.
Technical Analysis#
Code Path#
1. Entry point — Controller
def validation
response = Cupix::SiteinsightsService.validate_element_records(params[:facility_key], current_user: @current_user, current_team: @current_team)
render_json 200, response
end
클라이언트의 GET /api/v1/siteinsights/element_records/validation 요청을 처리한다.
2. Service orchestration
def validate_element_records(facility_key, current_user: nil, current_team: nil)
raise Cupix::Errors::Parameter.new(code: 'ARG10000', reason: 'facility_key is required') if facility_key.blank?
facility = ::FacilityRepository.new(current_user: current_user, current_team: current_team).show(facility_key)
element_counts = _calculate_element_counts(facility)
element_record_counts = _get_element_record_counts(facility_key, element_counts[:tasks_for_query])
stale_elements = _get_stale_elements_if_needed(facility, facility_key, element_record_counts)
result = _build_validation_result(facility_key, element_counts, element_record_counts, stale_elements)
Cupix::Logger.info('validate_element_records completed', class: self.name, function: __method__, facility_key: facility_key, is_valid: result[:is_valid], summary: result[:summary])
result
end
DB 조회 후 외부 서비스를 호출하는 순차적 흐름이다. _get_element_record_counts에서 에러가 발생하면 이후 로직이 모두 실행되지 않는다.
3. Failure point — 외부 API 호출
def _get_element_record_counts(facility_key, tasks_for_query)
return _empty_element_record_counts if tasks_for_query.empty?
response = RestClient.post(
"#{Cupix::Siteinsights.service_url}/element_records/validation",
{ facility_key: facility_key, tasks: tasks_for_query }.to_json,
content_type: :json
)
JSON.parse(response.body)['result']['data']
rescue RestClient::Exception => e
Cupix::Logger.error("failed to get validation data - error: #{e.message}", class: self.name, function: __method__, facility_key: facility_key)
raise Cupix::Errors::System.new(code: 'SYS20000', reason: "failed to get validation data - error: #{e.message}")
rescue JSON::ParserError => e
Cupix::Logger.error("failed to parse response body - error: #{e.message}", class: self.name, function: __method__, facility_key: facility_key)
raise Cupix::Errors::System.new(code: 'SYS20000', reason: "failed to parse response body - error: #{e.message}")
end
RestClient.post에 timeout 파라미터가 없다. AWS API Gateway는 기본 integration timeout이 29초이며, 백엔드(Lambda 또는 다른 서비스)가 이 시간 내에 응답하지 못하면 504를 반환한다.
4. Lambda / API Gateway Timeout 설정 불일치
이 /element_records/validation 엔드포인트를 처리하는 Lambda는 ElementRecordProxyFunction이다:
ElementRecordProxyFunction:
Type: AWS::Serverless::Function
Properties:
Handler: functions/api/ElementRecord.handler
FunctionName: !Sub "${Tenant}-${Environment}-${ServiceName}-ElementRecordProxy"
Timeout: 60
MemorySize: 1024
Lambda timeout은 60초로 설정되어 있다. 한편, API Gateway의 integration 정의에는 timeoutInMillis가 명시되어 있지 않다:
x-amazon-apigateway-integration:
type: "aws_proxy"
uri:
Fn::Sub: "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ElementRecordProxyFunction.Arn}/invocations"
httpMethod: "POST"
passThroughBehavior: "when_no_match"
payloadFormatVersion: "2.0"
AWS API Gateway REST API의 기본 integration timeout은 29초이다. 따라서 Lambda(60초)가 처리 중이더라도 API Gateway(29초)가 먼저 timeout을 반환하는 구조적 불일치가 존재한다.
| Component | Timeout | Source |
|---|---|---|
ElementRecordProxyFunction Lambda |
60s | template.yaml:651 |
| API Gateway integration (기본값) | 29s | api.yaml:260-266 — timeoutInMillis 미설정 |
RestClient.post (cupixworks-api) |
∞ (기본) | siteinsights_service.rb:275-279 — timeout 미설정 |
5. Service URL configuration
def service_url
case Cupix::Tesla.tenant
when 'cupix'
case Rails.env
when 'production'
case Cupix::Tesla.region_code
# ...
else
'https://6hdi0xzqqk.execute-api.us-west-2.amazonaws.com/api'
end
end
end
end
us-west-2 production 환경에서는 https://6hdi0xzqqk.execute-api.us-west-2.amazonaws.com/api/element_records/validation으로 호출한다.
6. 에러 전파 — Controller rescue
def redirect_to_502_error(exception)
raise_error(502, exception, code: 'BG10005', type: Cupix::Errors::BadGateway, reason: 'BadGateway', message: exception.message)
end
SYS20000 에러가 controller에서 502 Bad Gateway(BG10005)로 변환되어 클라이언트에 반환된다.
Log Evidence#
Datadog 검색 쿼리 — application error logs:
service:cupixworks-api status:error "failed to get validation data"
38건의 동일한 에러 로그가 확인되었다. 모든 로그의 속성:
{
"message": "failed to get validation data - error: 504 Gateway Timeout",
"class": "Cupix::SiteinsightsService",
"function": "_get_element_record_counts",
"facility_key": "umphr6",
"log_type": "custom"
}
Datadog 검색 쿼리 — request logs:
service:cupixworks-api "504"
39건의 request log가 확인되었으며 핵심 속성:
{
"controller": "Api::V1::ElementRecordsController#validation",
"status_code": 502,
"error_class": "Cupix::Errors::System",
"error_code": "SYS20000",
"duration_ms": 31600,
"db_time_ms": 1900,
"user": "yohan.kim@cupix.com",
"user_id": 5874,
"auth_method": "COGNITO",
"team_domain": "admin",
"team_id": 133
}
request duration이 평균 ~31,600ms로 일관되게 나타난다. DB time은 ~1,900ms이므로, 외부 서비스 호출에서 약 29,700ms (≈ API Gateway의 29초 timeout + 네트워크 오버헤드)가 소비된 것으로 판단된다.
Cross-service 검색 — downstream 에러 확인:
"validation data" status:error
cupixworks-api 외에 다른 서비스에서는 관련 에러가 발견되지 않았다. Siteinsights 백엔드 서비스는 Datadog에 별도로 인덱싱되지 않는 것으로 보인다 (AWS API Gateway + Lambda 구조 추정).
동일 시간대 다른 에러 확인:
service:cupixworks-api status:error
해당 시간 윈도우(22:00~23:10 UTC)에서 이 validation 에러 외 다른 에러 유형은 없었다.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | Siteinsights Lambda timeout(60s)과 API Gateway integration timeout(29s)의 불일치로 Lambda 처리 중 API Gateway가 먼저 504 반환 | template.yaml:651 Lambda Timeout: 60, api.yaml:260-266 timeoutInMillis 미설정(기본 29초). request duration ~31.6초가 API Gateway 29초 + 오버헤드에 정확히 부합. 3대 호스트, 3개 facility 모두 동일 패턴 |
— | Confirmed |
| H2 | cupixworks-api 측 네트워크 문제로 인한 연결 실패 |
us-west-2의 3대 인스턴스 모두 동일하게 실패 | 504는 connection failure가 아닌 gateway timeout. duration이 일관되게 ~30초로 네트워크 단절 패턴이 아님. 같은 시간대에 다른 에러 없음 | Rejected |
| H3 | 특정 facility의 대량 데이터로 인한 쿼리 성능 저하 | umphr6 facility가 29/38건(76%)으로 가장 많이 실패 |
3fq7tj(8건), 10ofni(1건)도 실패. 3개 facility 모두 실패하므로 특정 데이터 문제가 아닌 서비스 전체 문제 |
Rejected |
| H4 | RestClient 측 timeout 설정 부재로 인한 과도한 대기 | 코드에 명시적 timeout 미설정 (siteinsights_service.rb:275-279) |
504는 API Gateway가 먼저 timeout을 반환한 것이므로, RestClient timeout과 무관하게 발생함. 다만 timeout 부재는 별도 개선 필요 | Rejected (직접 원인 아님, 개선 필요) |
Fix Recommendation#
즉시 조치 (Critical)#
504 발생의 구조적 원인은 Lambda timeout(60s)과 API Gateway integration timeout(29s, 기본값)의 불일치이다. 두 가지 방향으로 해결 가능하다:
- 방안 A — API Gateway timeout 증가:
api.yaml의/element_records/validationintegration에timeoutInMillis: 60000을 추가하여 Lambda timeout(60s)과 일치시킨다. 단, API Gateway REST API의 최대 integration timeout은 29초이므로, 이를 넘기려면 API Gateway HTTP API로 전환하거나 다른 접근이 필요하다. - 방안 B — Lambda 처리 최적화:
ElementRecordProxyFunction의 validation 로직이 29초 내에 완료되도록 최적화한다 (MongoDB 쿼리 최적화, 인덱스 검토 등). app/services/cupix/siteinsights_service.rb:275-279:RestClient.post호출에 명시적timeout과open_timeout파라미터를 추가해야 한다. API Gateway의 29초 timeout보다 약간 긴 값(예: 35초)을 설정하여, API Gateway timeout 이후 불필요하게 대기하지 않도록 한다.- Siteinsights 백엔드 서비스 팀에 504 timeout 발생 사실을 공유하고, Lambda timeout vs API Gateway timeout 불일치 문제를 함께 검토해야 한다.
단기 개선 (1주 이내)#
- retry 로직 추가:
_get_element_record_counts에 exponential backoff를 적용한 retry를 1-2회 추가. 504는 일시적 문제일 수 있으므로 재시도가 효과적이다. - 에러 로그 레벨 검토: 외부 서비스 timeout은 transient한 운영 이슈일 수 있으므로, 첫 번째 시도 실패 시
warn, retry 후에도 실패 시error로 로깅하는 방식을 고려한다. - Circuit breaker 패턴 검토: 동일 서비스에 대한 반복 실패 시 빠르게 fallback하여 클라이언트 대기 시간을 줄인다.
장기 개선 (재발 방지)#
- Lambda/API Gateway timeout 정렬:
template.yaml:651의 Lambda timeout(60s)과api.yaml의 API Gateway integration timeout(기본 29s)을 일치시키는 정책을 수립한다. 다른 siteinsights 엔드포인트(GET/element_records, PUT/element_records/bulk등)도 동일한 불일치가 있을 수 있으므로 전체 점검이 필요하다. - Siteinsights 백엔드 서비스의 성능 모니터링 및 auto-scaling 검토 (Lambda의 경우 concurrency limit, provisioned concurrency 등).
- Siteinsights 서비스 로그를 Datadog에 인덱싱하여 end-to-end 추적 가능하도록 개선.
Monitoring#
- Siteinsights 외부 호출 성능 모니터링:
service:cupixworks-api "Cupix::SiteinsightsService" status:error
- Request duration 이상 감지 (p99 > 30초):
service:cupixworks-api @http.url_details.path:"/api/v1/siteinsights/element_records/validation" @duration:>30000000000
- 504 에러 발생률 알림:
service:cupixworks-api "504 Gateway Timeout"
Risk Assessment#
- Risk level: low — downstream 서비스의 일시적 성능 저하로 발생한 에러. 단일 사용자(admin)에게만 영향. 데이터 손실 없음.
- 예상 복잡도: standard — timeout 설정 추가와 retry 로직은 비교적 간단하나, downstream 서비스 성능 개선은 별도 팀 협업 필요.
Revision History#
Revision 1#
Feedback: siteinsights service lambda timeout 이랑 api gateway timeout 이 다른지 확인해줘
판정:
| 피드백 항목 | 판정 | 근거 |
|---|---|---|
| Lambda timeout과 API Gateway timeout이 다른지 확인 | 수용 | 다르다. ElementRecordProxyFunction Lambda timeout은 60초(template.yaml:651 — Timeout: 60), API Gateway integration timeout은 29초(기본값, api.yaml:260-266에 timeoutInMillis 미설정). Lambda는 60초까지 실행 가능하나 API Gateway가 29초에 먼저 504를 반환하는 구조적 불일치가 확인됨. 기존 RCA에서 "API Gateway 29초 timeout"만 언급하고 Lambda timeout과의 차이를 명시하지 않았던 부분을 보완. |
변경 사항:
- Root Cause Summary: Lambda timeout(60s)과 API Gateway integration timeout(29s) 불일치를 명시적으로 기술.
template.yaml:651,api.yaml:260-266코드 참조 추가. - Technical Analysis > Code Path: 새 섹션 "4. Lambda / API Gateway Timeout 설정 불일치" 추가 — Lambda 설정(
template.yaml:646-652)과 API Gateway integration 설정(api.yaml:260-266)의 코드 스니펫 및 timeout 비교 테이블 포함. - Hypotheses Considered: H1을 timeout 불일치 가설로 구체화.
- Fix Recommendation: API Gateway timeout 증가(방안 A)와 Lambda 처리 최적화(방안 B) 두 가지 방향을 제시. API Gateway REST API 최대 timeout(29초) 제약 사항 명시.
- 장기 개선: 다른 siteinsights 엔드포인트에도 동일한 timeout 불일치가 있을 수 있으므로 전체 점검 권장.
추가 조사 내용:
cupixworks/applications/siteinsights-service/template.yaml— Lambda 함수 정의 및 timeout 설정 확인cupixworks/applications/siteinsights-service/api.yaml— API Gateway OpenAPI 정의 및 integration 설정 확인 (timeoutInMillis 부재)cupixworks/applications/siteinsights-service/api.anchored.yaml— 동일 endpoint의 anchored API 정의 교차 확인