failed to generate thumbnail - error: Timed out connecting to server
RCA: failed to generate thumbnail - error: Timed out connecting to server
Overview#
What Happened#
2026-08-07 08:45 KST 경, production eu-central-1 리전의 cupixworks-worker (tesla Sidekiq) 호스트 하나(ip-10-1-80-125.eu-central-1.compute.internal)에서 pano 썸네일 생성 요청이 약 8초 사이 4건 연속 실패했다. 실패 원인은 tesla 가 thumbnail 생성 API Gateway (xntqdgslbc.execute-api.eu-central-1.amazonaws.com)로 보낸 HTTP POST 의 TCP connect timeout (Timed out connecting to server)이다. 이는 일시적 네트워크/인프라 이벤트이며, 호출 경로가 완전히 rescue 되어 사용자에게 노출되지 않았다 (썸네일이 재생성되지 않고 기존 썸네일이 유지될 뿐). 코드 결함이 아닌 transient noise 로 판정한다.
Quick Facts#
| Field | Value |
|---|---|
| exception.class | Cupix::Errors::System (SYS20000) wrapping RestClient::Exceptions::OpenTimeout |
| exception.message | failed to generate thumbnail - error: Timed out connecting to server |
| top_frame | app/services/cupix/thumbnail_service.rb:59 |
| runtime | Ruby / Sidekiq worker (tesla) |
| deploy | production-eu-central-1-20260806T1002Z0-9b66f324-cupixworks |
| env | production, region eu-central-1 |
Affected Teams#
| Team / Domain | Error Count | Impact |
|---|---|---|
| cupix (tenant) | 5 (14d, 4 in single burst) | pano 썸네일 재생성 1회 실패 → 기존 썸네일 유지, 사용자 영향 0 |
Timeline#
- 2026-07-31 21:52 KST — 동일 에러 1건 발생 (고립, 사전 징후).
- 2026-08-06 19:02 KST — 해당 리전 배포 (
...20260806T1002Z0-9b66f324). 회귀 원인 아님 (아래 참조). - 2026-08-07 08:45:35 KST — burst 시작, 4건 연속 실패 (모두 동일 호스트
ip-10-1-80-125). - 2026-08-07 08:45:43 KST — 마지막 발생 (cluster last_seen). 이후 재발 없음 = 자가 소멸.
Error Log#
failed to generate thumbnail - error: Timed out connecting to server
Impact#
- Service:
cupixworks-worker - Team: cupix
- 발생 횟수: 4 (cluster) / 5 (14d 로그 실측)
- 최초 발생: 2026-08-07 08:45 KST
- 최근 발생: 2026-08-07 08:45 KST
Root Cause Summary#
tesla 의 Cupix::ThumbnailService.generate_thumbnail! (thumbnail_service.rb:56)이 Cupix::HttpClient.post로 thumbnail 생성 API Gateway (https://xntqdgslbc.execute-api.eu-central-1.amazonaws.com/api/v1/thumbnails)에 POST 요청을 보내는 동안 TCP 연결 수립 자체가 timeout 되었다. RestClient 은 이를 RestClient::Exceptions::OpenTimeout 으로 raise 하며 그 default message 가 Timed out connecting to server 라는 고정 상수다. tesla 는 이를 rescue RestClient::Exception (thumbnail_service.rb:58)에서 Cupix::Errors::System SYS50000... 이 아닌 SYS20000 으로 재포장한다. 근본 원인은 특정 호스트↔API Gateway 구간의 일시적 네트워크 지연/불가용이며 (약 8초 창, 단일 호스트 한정, 이후 자가 소멸), tesla 코드 결함이 아니다. 다만 Cupix::HttpClient 의 재시도가 HTTP status code 기반([429,502,503,504])이라 status 가 없는 connect timeout 은 재시도되지 않는 resilience gap 이 존재한다 — 이는 결함이 아닌 개선 여지다.
Technical Analysis#
Code Path#
- Entry point:
app/workers/concerns/thumbnail_change_worker/pano.rb:9—ThumbnailChangeWorker::Pano#perform(entity_id)가Pano.find_by_id후entity.change_thumbnail_job호출. app/models/concerns/thumbnailable.rb:104-110—change_thumbnail_job은change_thumbnail_job!을 호출하되rescue StandardError → false로 모든 예외를 흡수.
def change_thumbnail_job
change_thumbnail_job!
rescue StandardError
false
else
true
end
app/models/concerns/thumbnailable.rb:83-96—change_thumbnail_job!이Cupix::ThumbnailService.generate_thumbnail(source:, target:, size:)호출 (pano 리소스 → S3 target.../thumbnail/panos/{id}).app/services/cupix/thumbnail_service.rb:16-24—generate_thumbnail(non-bang)이generate_thumbnail!을 호출하고rescue Cupix::Errors::System → error 로깅 후 false반환. 이것이 관측된 error 로그를 남기는 지점.
def generate_thumbnail(source: nil, target: nil, size: [300, 200])
generate_thumbnail!(source: source, target: target, size: size)
rescue Cupix::Errors::System => e
Cupix::Logger.error(e.reason, class: self.name, function: __method__, thumbnail_source: source, thumbnail_target: target, width: size[0], height: size[1])
false
else
true
end
- Failure point:
app/services/cupix/thumbnail_service.rb:56-60—Cupix::HttpClient.post가 connect timeout 으로 raise →rescue RestClient::Exception이Cupix::Errors::SystemSYS20000 으로 재포장.
Cupix::Logger.info('Thumbnail Requested', class: self.name, function: __method__, thumbnail_source: source, thumbnail_target: target, width: width, height: height)
response = Cupix::HttpClient.post(Cupix::Thumbnail.service_url, body.to_json, _header)
Cupix::Logger.info('Thumbnail Generated', class: self.name, function: __method__, thumbnail_source: source, thumbnail_target: target, width: width, height: height)
rescue RestClient::Exception => e
raise Cupix::Errors::System.new(code: 'SYS20000', reason: "failed to generate thumbnail - error: #{e.message}")
lib/cupix/http_client.rb:34-46—HttpClient.post의 재시도는e.http_code가RETRIABLE_STATUS_CODES = [429, 502, 503, 504]에 포함될 때만 동작. connect timeout(OpenTimeout)은 HTTP 응답이 없어e.http_code가 nil → 조건 불만족 → 재시도 없이 즉시 re-raise.
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
기대 동작 vs 실제 동작: 기대 — thumbnail API Gateway 로 POST 후 200 수신, 썸네일 갱신. 실제 — TCP 연결 수립 단계에서 timeout → OpenTimeout raise → status code 없어 HttpClient 재시도 미발동 → SYS20000 으로 재포장 → generate_thumbnail/change_thumbnail_job 이 완전히 rescue → 썸네일 미갱신(기존 유지), error 로그만 발생. 사용자 영향 없음.
RestClient::Exceptions::OpenTimeout 은 rest-client 상 Net::OpenTimeout 을 래핑하며 default message 가 Timed out connecting to server 로 고정된 상수다 (sorbet rbi rest-client*.rbi:428-432). 따라서 대표 메시지는 변형이 아니라 신뢰 가능한 고정 문자열이다.
Log Evidence#
사용한 Datadog 쿼리:
service:cupixworks-worker "failed to generate thumbnail" "Timed out connecting to server"
14일 창 실측 5건 — 4건이 단일 burst(2026-08-07 08:45 KST), 1건은 2026-07-31 21:52 KST 고립 발생:
2026-08-07 08:45:43 error failed to generate thumbnail - error: Timed out connecting to server Cupix::ThumbnailService#generate_thumbnail
2026-08-07 08:45:39 error failed to generate thumbnail - error: Timed out connecting to server Cupix::ThumbnailService#generate_thumbnail
2026-08-07 08:45:35 error failed to generate thumbnail - error: Timed out connecting to server Cupix::ThumbnailService#generate_thumbnail
2026-08-07 08:45:35 error failed to generate thumbnail - error: Timed out connecting to server Cupix::ThumbnailService#generate_thumbnail
2026-07-31 12:52:18 error failed to generate thumbnail - error: Timed out connecting to server Cupix::ThumbnailService#generate_thumbnail
대표 로그의 raw attributes (burst 첫 건):
{
"message": "failed to generate thumbnail - error: Timed out connecting to server",
"status": "error",
"class": "Cupix::ThumbnailService",
"function": "generate_thumbnail",
"tenant": "cupix",
"environment": "production",
"version": "production-eu-central-1-20260806T1002Z0-9b66f324-cupixworks",
"host": "ip-10-1-80-125.eu-central-1.compute.internal",
"thumbnail_target": {
"bucket": "cupix-tesla-eu-static",
"s3_bucket_region": "eu-central-1",
"key": "8c00ff6b201fe746/thumbnail/panos/11047741"
},
"thumbnail_source": {
"bucket": "cupixworks-source-eu2defghijkl-mece2",
"s3_bucket_region": "me-central-2"
}
}
증거 해석: 4건 burst 는 모두 동일 호스트(ip-10-1-80-125), 약 8초 창(08:45:35–08:45:43)에 집중 → 단일 워커 호스트↔eu-central-1 API Gateway 구간의 일시적 네트워크 이벤트. region:eu-central-1, tenant cupix → Cupix::Thumbnail.service_url (lib/cupix/thumbnail.rb:33-34)의 euce1 분기 https://xntqdgslbc.execute-api.eu-central-1.amazonaws.com/api/v1/thumbnails 이 대상. target 은 pano 썸네일. status-board (svc:cupixworks-worker::unknown) active/recent 모두 null.
동일 endpoint 의 별개 변형 — 500 Internal Server Error (14d 50+건) 는 downstream thumbnail Lambda 실행 실패로, 이 timeout cluster 와 다른 ET issue 이므로 cross-merge 하지 않는다:
2026-08-07 02:23:35 error failed to generate thumbnail - error: 500 Internal Server Error
2026-08-07 00:13:41 error failed to generate thumbnail - error: 500 Internal Server Error
Hypotheses Considered#
| # | Hypothesis | Evidence for | Evidence against | Verdict |
|---|---|---|---|---|
| H1 | thumbnail API Gateway 로의 TCP connect timeout (일시적 네트워크/인프라) 이 root cause, 코드 결함 아님 | 메시지 = RestClient::Exceptions::OpenTimeout 고정 default message (rest-client*.rbi:428-432); 4건 burst 가 단일 호스트 8초 창 집중; 이후 자가 소멸; region:eu-central-1 = xntqdgslbc.execute-api euce1 endpoint |
— | Confirmed |
| H2 | 2026-08-06 배포 회귀 (...20260806T1002Z0-9b66f324) |
burst 가 배포일 이후 발생 | 2026-07-31 에 이미 동일 에러 1건; timeout 은 코드가 아닌 전송 계층; thumbnail_service.rb/http_client.rb 배포 코드에 timeout 유발 변경 없음 (git log 상 마지막 변경 = TSLA-12414 2026-04) |
Rejected |
| H3 | downstream thumbnail Lambda 결함 (500 과 동일 원인) | 같은 endpoint 에서 500 다발 | 500 은 HTTP 응답을 받은 것(Lambda 도달); timeout 은 연결 수립 실패로 Lambda 도달 전 단계 → 다른 계층·다른 ET issue, cross-merge 금지 | Rejected |
| H4 | Cupix::HttpClient 재시도 미발동이 이 에러를 유발하는 버그 |
OpenTimeout.http_code = nil → RETRIABLE_STATUS_CODES 불포함 → 재시도 안 됨 (http_client.rb:39) |
재시도가 있어도 8초간 지속된 연결 불가는 재시도로도 실패했을 가능성 높음; 재시도 부재는 결함이 아닌 resilience gap (status 없는 예외의 설계적 미포함, net/http timeout 계열과 동형) | Rejected (gap, not bug) |
Fix Recommendation#
즉시 조치 (Critical)#
없음. 호출 경로가 change_thumbnail_job (thumbnailable.rb:106)와 generate_thumbnail (thumbnail_service.rb:18) 이중으로 완전히 rescue 되어 사용자 영향이 0 이고, 근본 원인이 일시적 네트워크 이벤트이므로 코드 변경이 필요 없다. ET IGNORE 권장.
단기 개선 (1주 이내, 선택)#
lib/cupix/http_client.rb— 재시도 대상을 HTTP status code 뿐 아니라 transport-level 예외(RestClient::Exceptions::OpenTimeout,RestClient::Exceptions::ReadTimeout,RestClient::Exceptions::ConnectionError등)로 확장하는 방향. 단, POST 는 idempotent 하지 않으므로(중복 썸네일 생성 위험) POST 경로 재시도는 신중해야 한다. thumbnail 생성은 target key 가 결정적(.../thumbnail/panos/{id})이라 재실행이 사실상 idempotent 이므로 이 경로에 한해 connect-timeout 재시도를 허용하는 방안이 안전하다.app/services/cupix/thumbnail_service.rb:19— 일시적 transport timeout 은error가 아닌warn레벨로 로깅하도록 예외 클래스별로 좁혀 alarm noise 를 줄이는 방안 (500/downstream 실패는error유지).
장기 개선 (재발 방지)#
- thumbnail 생성은 이미 background worker(
ThumbnailChangeWorker::Pano,retry: 1)로 처리되므로, Sidekiq 재시도 정책을 활용해 transport 실패 시 예외를 re-raise 하여 워커 재시도에 위임하는 구조를 검토 (현재는change_thumbnail_job의rescue StandardError → false가 예외를 삼켜 Sidekiq 재시도가 발동하지 않음).
Monitoring#
thumbnail connect-timeout 발생 추이:
service:cupixworks-worker status:error "failed to generate thumbnail - error: Timed out connecting to server"
thumbnail 생성 실패 전체(모든 변형) 추이:
service:cupixworks-worker status:error "failed to generate thumbnail"
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial (조치 불필요 / 선택적 개선만)