Api::V1::IntegrationsController#create (avg 4295ms, max 4295ms)
RCA: IntegrationsController#create Latency (4295ms)
Overview#
What Happened#
2026-05-30 04:12 KST에 cupixworks-api 서비스의 Api::V1::IntegrationsController#create 엔드포인트에서 단일 요청이 4295ms 소요되었다. facility e73mj에 대한 새로운 외부 서비스 통합 생성 요청으로, 외부 OAuth 토큰 교환 API 호출이 동기적으로 수행되면서 발생한 latency 이슈이다.
Quick Facts#
| Field | Value |
|---|---|
| resource_name | Api::V1::IntegrationsController#create |
| top_frame | app/concerns/parameter/integration.rb:54 |
| env | production, us-west-2 |
| avg_duration_ms | 4295 |
| sample_trace_id | 3548064727843788619 |
Timeline#
- 2026-05-30 04:12:08 KST —
IntegrationsController#create요청 수신 (facilitye73mj) - 2026-05-30 04:12:14 KST — 요청 완료 (200 OK, ~4295ms 소요)
- 2026-05-30 04:12:16 KST — 동일 facility에서
bim360/access_token호출 (통합 정상 사용 시작) - 2026-05-30 04:12:28 KST — BIM360 integration update 호출
Error Log#
{
"resource_name": "Api::V1::IntegrationsController#create",
"service": "cupixworks-api",
"occurrences": 1,
"avg_ms": 4295,
"max_ms": 4295,
"sample_trace_id": "3548064727843788619"
}
Impact#
- Service:
cupixworks-api - 발생 횟수: 1
- 최초 발생: 2026-05-30 04:12 KST
- 최근 발생: 2026-05-30 04:12 KST
Root Cause Summary#
IntegrationsController#create는 새 integration 생성 시 외부 OAuth 프로바이더(BIM360/Procore/OPC 등)에 동기적으로 토큰 교환 HTTP 요청을 수행한다. Cupix::HttpClient는 RestClient를 사용하면서 명시적 timeout을 설정하지 않아 외부 API의 응답 지연이 그대로 요청 latency로 전가된다. OPC 프로바이더의 경우 validate_opc_params!에서 2회의 연속 외부 HTTP 호출(OAuth 토큰 + OPC API 토큰)을 검증 목적으로 추가 수행하며, 이 모든 호출에 retry 로직(최대 3회, 지수 백오프)이 적용되어 worst case에서 수십 초까지 지연될 수 있다. 4295ms는 외부 OAuth API의 느린 응답(또는 1회 retry 후 성공)에 의한 것으로 판단된다.
Technical Analysis#
Code Path#
- Entry point:
app/controllers/api/v1/integrations_controller.rb:19 - Factory 호출:
app/factories/integration_factory.rb:5—create!(params)호출 - OPC 프로바이더인 경우 검증:
app/factories/integration_factory.rb:84-103—validate_opc_params!에서 2회 외부 HTTP 호출 - Parameter 설정 (모든 프로바이더):
app/concerns/parameter/integration.rb:54—operation_class.get_token(code, region)외부 HTTP 호출 - HTTP 클라이언트:
lib/cupix/http_client.rb:34-46—RestClient.post에 timeout 미설정, retry 3회 - Model 저장:
app/factories/base_factory.rb:124—model.save!
def create
@model = factory_instance.create!(params)
super
end
elsif params[:code].present?
token = operation_class.get_token(params[:code], params[:region])
이 라인에서 외부 OAuth API에 동기 HTTP 요청을 보내며, 응답이 올 때까지 요청 스레드가 블로킹된다.
def self.post(url, payload, headers = {}, retries: MAX_RETRIES)
attempt = 0
begin
RestClient.post(url, payload, headers)
rescue RestClient::Exception => e
if RETRIABLE_STATUS_CODES.include?(e.http_code) && attempt < retries
attempt += 1
sleep((2**(attempt - 1)) + rand(0.0..0.5))
retry
end
raise
end
end
RestClient.post에 timeout 파라미터가 설정되어 있지 않다. RestClient의 기본 timeout은 무제한(nil)이므로, 외부 서버의 응답이 느리면 무한 대기할 수 있다.
OPC 프로바이더의 경우 validate_opc_params!에서 추가 2회 외부 호출:
# Step 2: Validate OAuth2.0 credentials by attempting to get access token
begin
oauth_response = OpcOperation.get_access_token(
access_token_url: opc_params[:oic_oauth_token_url],
client_id: opc_params[:client_id],
client_secret: opc_params[:client_secret],
scope: opc_params[:scope]
)
# Step 3: Validate OPC API access token endpoint
OpcOperation.get_opc_api_access_token(
opc_api_url: opc_params[:opc_access_token_url],
oauth_access_token: oauth_response['access_token']
)
Log Evidence#
Datadog 쿼리:
service:cupixworks-api "IntegrationsController#create"
Time: 2026-05-29T17:00:00Z to 2026-05-29T20:00:00Z
해당 시간대에 #create 호출은 단 1건만 발견:
{
"timestamp": "2026-05-30 04:12:14",
"status": "info",
"message": "[200] POST /api/v1/facilities/e73mj/integrations (Api::V1::IntegrationsController#create)"
}
동일 facility에서 BIM360과 Procore integration이 이미 활성 상태였음을 확인:
{
"timestamp": "2026-05-30 04:12:40",
"status": "info",
"message": "[200] POST /api/v1/facilities/e73mj/integrations/procore/access_token (Api::V1::IntegrationsController#access_token)"
}
{
"timestamp": "2026-05-30 04:12:46",
"status": "info",
"message": "[200] POST /api/v1/facilities/e73mj/integrations/bim360/access_token (Api::V1::IntegrationsController#access_token)"
}
에러 로그 검색 (결과 없음):
service:cupixworks-api status:error "IntegrationsController"
Time: 2026-05-29T17:00:00Z to 2026-05-29T20:00:00Z
Result: 0 logs
에러 없이 200 OK로 응답했으나 4295ms 소요. 에러 로그가 없으므로 retry 없이 외부 API의 단순 응답 지연으로 판단.
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | 외부 OAuth API 응답 지연 (BIM360/Procore/OPC token endpoint slow response) | 4295ms 전체가 단일 요청에 집중됨. Cupix::HttpClient에 timeout 미설정. 에러 로그 없음 (retry 없이 1회 호출로 성공). 코드 경로상 외부 HTTP 호출이 유일한 blocking I/O. |
정확히 어떤 provider인지 로그에서 확인 불가 (create 시 provider-specific 로그 미출력) | Confirmed |
| H2 | DB 쿼리 또는 Elasticsearch 인덱싱 지연 | model.save! 후 Elasticsearch indexing 호출 가능성 존재 |
Elasticsearch 인덱싱은 비동기. 단일 레코드 저장에 4초는 비현실적. 다른 요청(#index, #access_token)은 정상 응답 |
Rejected |
| H3 | Retry 로직에 의한 누적 지연 (429/502/503/504 → retry 1-2회) | Cupix::HttpClient에 retry 로직 존재. 1회 retry 시 1 + rand(0..0.5) 초 sleep 후 재시도 → 총 ~4초 가능 |
에러 로그 미출력 (retry 발생 시에도 최종 성공이면 에러 로그 없음). 가능하나 단순 slow response가 더 likely | Inconclusive |
Fix Recommendation#
즉시 조치 (Critical)#
lib/cupix/http_client.rb—RestClient.post/get호출에timeout및open_timeout파라미터 추가 (예:open_timeout: 5, timeout: 10)- 이를 통해 외부 API 지연 시 최대 대기 시간을 제한
단기 개선 (1주 이내)#
app/concerns/parameter/integration.rb:54— 토큰 교환 호출에 개별 timeout 설정 또는 circuit breaker 패턴 적용app/factories/integration_factory.rb:84-97— OPCvalidate_opc_params!의 2회 연속 외부 호출을 병렬화하거나 비동기 검증으로 전환 검토- APM span에서 외부 HTTP 호출 시간을 분리 측정할 수 있도록 계측(instrumentation) 추가
장기 개선 (재발 방지)#
- 외부 OAuth 토큰 교환을 비동기 worker로 이동하고, integration 상태를
pending→active로 전이시키는 패턴 도입 - 외부 API 호출에 대한 SLO 설정 및 모니터링 (p95 latency threshold)
Monitoring#
- APM 기반 모니터링:
resource_name:Api::V1::IntegrationsController#create의 p95 latency > 3000ms 시 알림 - 메트릭 쿼리 예시:
avg:trace.rack.request.duration{service:cupixworks-api,resource_name:api::v1::integrationscontroller_create} > 3000000000
Risk Assessment#
- Risk level: low
- 예상 복잡도: standard
- 사용자 영향: 단발성 이벤트(1건). integration 생성은 빈번하지 않은 작업이나, timeout 미설정으로 인해 외부 API 장애 시 무한 대기 위험이 잠재되어 있음.