ES /docs

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#

  1. 2026-05-30 04:12:08 KSTIntegrationsController#create 요청 수신 (facility e73mj)
  2. 2026-05-30 04:12:14 KST — 요청 완료 (200 OK, ~4295ms 소요)
  3. 2026-05-30 04:12:16 KST — 동일 facility에서 bim360/access_token 호출 (통합 정상 사용 시작)
  4. 2026-05-30 04:12:28 KST — BIM360 integration update 호출

Error Log#

Datadog Logs

json
{
  "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::HttpClientRestClient를 사용하면서 명시적 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:5create!(params) 호출
  • OPC 프로바이더인 경우 검증: app/factories/integration_factory.rb:84-103validate_opc_params!에서 2회 외부 HTTP 호출
  • Parameter 설정 (모든 프로바이더): app/concerns/parameter/integration.rb:54operation_class.get_token(code, region) 외부 HTTP 호출
  • HTTP 클라이언트: lib/cupix/http_client.rb:34-46RestClient.post에 timeout 미설정, retry 3회
  • Model 저장: app/factories/base_factory.rb:124model.save!
app/controllers/api/v1/integrations_controller.rb:19-23ruby
def create
  @model = factory_instance.create!(params)

  super
end
app/concerns/parameter/integration.rb:53-54ruby
elsif params[:code].present?
  token = operation_class.get_token(params[:code], params[:region])

이 라인에서 외부 OAuth API에 동기 HTTP 요청을 보내며, 응답이 올 때까지 요청 스레드가 블로킹된다.

lib/cupix/http_client.rb:34-46ruby
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.posttimeout 파라미터가 설정되어 있지 않다. RestClient의 기본 timeout은 무제한(nil)이므로, 외부 서버의 응답이 느리면 무한 대기할 수 있다.

OPC 프로바이더의 경우 validate_opc_params!에서 추가 2회 외부 호출:

app/factories/integration_factory.rb:84-97ruby
# 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 쿼리:

text
service:cupixworks-api "IntegrationsController#create"
Time: 2026-05-29T17:00:00Z to 2026-05-29T20:00:00Z

해당 시간대에 #create 호출은 단 1건만 발견:

json
{
  "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이 이미 활성 상태였음을 확인:

json
{
  "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)"
}
json
{
  "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)"
}

에러 로그 검색 (결과 없음):

text
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.rbRestClient.post/get 호출에 timeoutopen_timeout 파라미터 추가 (예: open_timeout: 5, timeout: 10)
  • 이를 통해 외부 API 지연 시 최대 대기 시간을 제한

단기 개선 (1주 이내)#

  • app/concerns/parameter/integration.rb:54 — 토큰 교환 호출에 개별 timeout 설정 또는 circuit breaker 패턴 적용
  • app/factories/integration_factory.rb:84-97 — OPC validate_opc_params!의 2회 연속 외부 호출을 병렬화하거나 비동기 검증으로 전환 검토
  • APM span에서 외부 HTTP 호출 시간을 분리 측정할 수 있도록 계측(instrumentation) 추가

장기 개선 (재발 방지)#

  • 외부 OAuth 토큰 교환을 비동기 worker로 이동하고, integration 상태를 pendingactive로 전이시키는 패턴 도입
  • 외부 API 호출에 대한 SLO 설정 및 모니터링 (p95 latency threshold)

Monitoring#

  • APM 기반 모니터링: resource_name:Api::V1::IntegrationsController#create 의 p95 latency > 3000ms 시 알림
  • 메트릭 쿼리 예시:
text
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 장애 시 무한 대기 위험이 잠재되어 있음.