ES /docs

BaseService::handlingMessageErrors | Error and message object - {"error":{"statusCode":400,"requestU

RCA: Unknown column 'spacetimes.summary_text' on spacetime summary write

Overview#

What Happened#

capture-intelligence-agent 가 LLM 분석 결과를 spacetime 에 기록하려고 PUT /api/v1/facilities/{key}/spacetimes/{id} 를 호출하면 tesla 가 Mysql2::Error: Unknown column 'spacetimes.summary_text' in 'field list' 로 실패했다. 2026-08-06 14:06 KST 부터 약 1시간 동안 production 에서만 발생했고, 13 개 facility 의 15 개 spacetime 에 걸쳐 나타났다. tesla 의 production DB 에 spacetimes.summary_text 컬럼이 존재하지 않아 UPDATE 가 거부되면서, 해당 spacetime 의 AI 요약이 저장되지 못하고 summary_stateerror 로 기록되었다.

Quick Facts#

Field Value
exception.class Cupix::Errors::Parameter (ARG10001), 원인 Mysql2::Error
exception.message Mysql2::Error: Unknown column 'spacetimes.summary_text' in 'field list'
top_frame app/repositories/facility_repository.rb:132 (write), :143 (rescue→raise)
deploy tesla production (master), 컬럼 미적용 스키마
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
whitingturner (tenant cupix) 29 (tesla 400), 87 (agent 로그) spacetime 단위 AI 요약 저장 실패, summary_state=error 로 마킹

13 개 distinct facility, 15 개 distinct spacetime 에 걸쳐 발생했다. 단일 레코드 재진입이 아니라 스키마 전역 문제다.

Timeline#

  1. 2026-03-09 22:55 KST — 마이그레이션 20260306071939_add_summary_to_spacetimes 가 커밋됨. 파일 버전은 20260306071939 로 backdate 됨 (8621b4849, TSLA-12069).
  2. 2026-03-09 22:55 KST — 같은 날 상위 버전 마이그레이션 20260309000001_add_summary_state_columns 도 커밋됨 (bba57b598).
  3. 2026-07-06 13:31 KST — schema drift 진단 및 수정 커밋 707aac515 (TSLA-13490) 이 브랜치 origin/jarvis/TSLA-13490 에 작성됨. develop/master 에는 미병합.
  4. 2026-08-06 14:06 KST — production 에서 첫 [400] PUT update_spacetime 발생 (first_seen).
  5. 2026-08-06 14:51 KST — cluster last_seen.
  6. 2026-08-06 15:08 KST — 마지막 tesla 400 로그 (06:08:16 UTC). 이후 해당 창에서 관측 종료.

Error Log#

Datadog Logs

text
BaseService::handlingMessageErrors | Error and message object - {"error":{"statusCode":400,"requestUriHref":"http://api-tesla.cupix.internal/api/v1/facilities/rk93lz/spacetimes/1525775?fields%5B0%5D=id&fields%5B1%5D=summary&fields%5B2%5D=summary_state&fields%5B3%5D=facility&fields%5B4%5D=record&fields%5B5%5D=level","bodyResult":{"code":"ARG10001","type":"Cupix::Errors::Parameter","reason":"Invalid argument","message":"Mysql2::Error: Unknown column 'spacetimes.summary_text' in 'field list'"},"modelId":749925},"sqsMessage":{"MessageId":"92d5fddb-333f-4ada-b7af-f18cddc67e1f","Attributes":{"ApproximateReceiveCount":"1"}}}

Impact#

  • Service: cupixworks-capture-intelligence-agent (원인은 downstream cupixworks-api / tesla)
  • Team: whitingturner
  • 발생 횟수: 30 (collector 샘플), 실측 tesla 400 로그 29 건 + agent 로그 87 건 (2026-08-06 14:06 - 15:08 KST 창)
  • 최초 발생: 2026-08-06 14:06 KST
  • 최근 발생: 2026-08-06 14:51 KST

Root Cause Summary#

tesla production DB 의 spacetimes 테이블에 summary_text 컬럼이 존재하지 않는 schema drift 가 원인이다. summary/summary_text 를 추가하는 마이그레이션 20260306071939_add_summary_to_spacetimes 는 2026-03-09 에 커밋되면서 파일 버전이 20260306071939 로 backdate 되었다. Rails 는 schema_migrations 의 max 버전보다 큰 마이그레이션만 실행하므로, 같은 날 커밋된 상위 버전 20260309000001 을 먼저 적용한 환경은 backdate 된 20260306071939 를 영구히 건너뛰었다. 그 결과 이 환경들은 summary/summary_state 는 있지만 summary_text 는 없는 상태가 되었고, db/schema.rb 는 재생성되어 summary_text 를 포함하므로 코드 리뷰에서는 드러나지 않았다. capture-intelligence-agent 가 요약을 기록할 때 tesla update_spacetimespacetime.summary_text = spacetime.summary_text_for_search 를 세팅하고 save! 하면서 없는 컬럼에 UPDATE 를 발행 → Mysql2::Error: Unknown columnrescue StandardErrorARG10001 로 재포장 → HTTP 400. 이 drift 를 고치는 idempotent 재적용 마이그레이션(TSLA-13490)은 브랜치 origin/jarvis/TSLA-13490 에만 있고 develop/master 에 병합되지 않아 production 에 배포된 적이 없다.

Technical Analysis#

Code Path#

Entry point: agent 의 요약 완료 write.

applications/agents/packages/cupix-capture-intelligence-agent/src/capture-intelligence-service.ts:185-190typescript
logger.info('CaptureIntelligenceService::updateSpacetimeSummary | spacetimeId: %d, summary: %s',
	spacetimeId, describeSummary(summary));
await this.cupixApi.spacetime.update(facilityKey, spacetimeId, {
	summary,
	summary_state: state
});

summary 가 함께 전달되므로 tesla update_spacetime 의 summary 분기가 실행된다.

app/repositories/facility_repository.rb:128-138ruby
    if params[:summary].present?
      summary = params[:summary].is_a?(ActionController::Parameters) ? params[:summary].permit!.to_h : params[:summary]
      spacetime.validate_summary!(summary)
      spacetime.summary = summary
      spacetime.summary_text = spacetime.summary_text_for_search
    end

    spacetime.summary_state = params[:summary_state] if params.key?(:summary_state)

Failure point: save! 가 없는 컬럼에 UPDATE 를 발행하고, generic rescue 가 이를 400 으로 재포장한다.

app/repositories/facility_repository.rb:142-146ruby
    begin
      spacetime.save!
    rescue StandardError => e
      raise Cupix::Errors::Parameter.new(code: 'ARG10001', reason: 'Invalid argument', message: e.message)
    end

기대 동작: summary_text 컬럼에 검색용 평문을 저장하고 200 을 반환한다. 실제 동작: production DB 에 컬럼이 없어 Mysql2::Error: Unknown column 'spacetimes.summary_text' in 'field list' 가 발생하고, 400 (ARG10001) 으로 응답한다.

drift 의 근거는 스키마 정의와 실제 적용의 불일치다. masterdb/schema.rb 에는 spacetimes 블록에 summary_text 가 없지만, 컬럼을 추가하는 마이그레이션 파일은 존재한다.

db/migrate/20260306071939_add_summary_to_spacetimes.rb:3-9ruby
class AddSummaryToSpacetimes < ActiveRecord::Migration[7.2]
  def change
    change_table :spacetimes, bulk: true do |t|
      t.json :summary
      t.text :summary_text
    end
  end
end

TSLA-13490 이 작성한 수정 마이그레이션은 이 backdate 메커니즘을 문서화하고 idempotent 하게 재적용한다. 다만 이 파일은 develop/master 에 병합되지 않았다.

db/migrate/20260613000000_readd_summary_columns_to_spacetimes.rb:16-24 (origin/jarvis/TSLA-13490)ruby
class ReaddSummaryColumnsToSpacetimes < ActiveRecord::Migration[7.2]
  def change
    change_table :spacetimes, bulk: true do |t|
      t.json :summary unless column_exists?(:spacetimes, :summary)
      t.text :summary_text unless column_exists?(:spacetimes, :summary_text)
    end
  end
end

agent 쪽은 이 400 을 수신해 로깅하고 SQS 메시지를 삭제한 뒤 updateErrorStatesummary_stateerror 로 기록한다 (base-service.ts:300-311, capture-intelligence-service.ts:198-204). 즉 요약 결과가 폐기된다.

Log Evidence#

tesla 400 요청 로그 쿼리:

text
service:cupixworks-api "spacetimes.summary_text"

결과: 29 건, 100% production, 100% PUT ... #update_spacetime. GET (show_spacetime) 에서는 0 건.

text
[400] PUT /api/v1/facilities/rk93lz/spacetimes/1525775 (Api::V1::FacilitiesController#update_spacetime)
[400] PUT /api/v1/facilities/m30gmo/spacetimes/1524911 (Api::V1::FacilitiesController#update_spacetime)

전체 언급 (agent + api) 쿼리:

text
"Unknown column 'spacetimes.summary_text'"

결과: 116 건 (agent 87 + api 29), 100% production, 창 2026-08-06 05:06:14 UTC - 06:08:16 UTC (14:06 - 15:08 KST).

agent 쪽 원문 (warn 단계, 400 수신 시점):

text
CupixAuth::handleError | Response statusCode: 400, requestUriHref: http://api-tesla.cupix.internal/api/v1/facilities/rk93lz/spacetimes/1525775?fields%5B0%5D=id&fields%5B1%5D=summary&fields%5B2%5D=summary_state&fields%5B3...

captures.summary_text 는 동일 창에서 0 건이었다. captures 테이블은 컬럼이 정상 적용되어 있고, spacetimes 만 drift 되었음을 확인한다.

text
service:cupixworks-api "captures.summary_text"

영향 범위: 13 개 distinct facility (rk93lz, m30gmo, yxhl8x, ywdtr, fgxxfo, psec02, 5ivznb, 2oaypp, hox1e3, umphr6 등), 15 개 distinct spacetime (1525775, 1524911, 1525824, ...). 특정 레코드 재진입이 아닌 스키마 전역 결정론적 실패다.

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 production spacetimessummary_text 컬럼이 없는 schema drift (backdate 된 마이그레이션이 skip 됨) master db/schema.rb 의 spacetimes 블록에 summary_text 없음 (grep count 0), 마이그레이션 20260306071939 는 존재하나 파일 버전이 커밋일(2026-03-09)보다 backdate 됨; TSLA-13490 커밋 메시지가 동일 메커니즘 진단; 13 facility/15 spacetime 전역 결정론적 실패 Confirmed
H2 배포 cutover 중 stale schema cache 로 인한 transient 오류 (ispring_user_id 4b1f8e9b 유형) 05:xx UTC 시간창 집중, ispring 버스트와 동일 창 이 케이스는 destructive migration(컬럼 삭제)이 아니라 additive migration 이 아예 적용 안 됨; TSLA-13490 이 "dev-only schema drift, not transient" 로 명시; captures 는 정상이고 spacetimes 만 실패 = 캐시 문제라면 이런 선택적 실패 안 나옴 Rejected
H3 애플리케이션 코드가 잘못된 컬럼을 참조 (코드 결함) Unknown column 메시지 summary_textsummary_text_for_search/검색 필터에서 정당하게 참조됨 (facility_repository.rb:69,132, clearable/spacetime.rb:11); develop db/schema.rb:4126 에 컬럼 정의 존재; 코드는 스키마 정의와 일치 Rejected
H4 이 클러스터가 동일 창의 ispring_user_id(ET acdd2caa) / users/records Unknown column 버스트와 같은 이슈 같은 2026-08-06 05:xx UTC 창, 같은 Unknown column ... field list 형태 컬럼/테이블/서비스 상이 (spacetimes vs users vs records), Datadog ET 는 컬럼별 별개 이슈로 집계; captures.summary_text 는 0 건 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • schema drift 수정 마이그레이션을 develop/master 에 병합하고 production 에 배포한다. 이미 작성된 db/migrate/20260613000000_readd_summary_columns_to_spacetimes.rb (브랜치 origin/jarvis/TSLA-13490, TSLA-13490) 가 column_exists? 가드로 idempotent 하게 summary/summary_text 를 재적용한다. 컬럼이 있는 환경에서는 no-op, drift 된 production DB 에서는 컬럼을 복구한다. 신규 코드 작성 없이 기존 PR 병합으로 해소된다.
  • 병합 전 확인: git merge-base --is-ancestor 707aac515 origin/develop 이 false 이므로 (미병합) 별도 신규 마이그레이션을 만들지 말고 이 PR 을 병합한다.

단기 개선 (1주 이내)#

  • update_spacetimerescue StandardError => e (facility_repository.rb:143) 는 스키마 오류 같은 서버측 결함까지 ARG10001 (client 400) 으로 분류한다. Mysql2::Error/ActiveRecord::StatementInvalid 는 400 이 아닌 500 로 매핑해 client 오류로 오분류되지 않도록 rescue 를 좁힌다. 그래야 이런 스키마 결함이 client 입력 오류로 위장되지 않고 알람에 노출된다.

장기 개선 (재발 방지)#

  • 배포 파이프라인에 db/schema.rb 와 실제 DB 컬럼 정합성 검증 스텝을 추가한다. drift 의 근본 원인은 backdate 된 마이그레이션 버전이므로, 마이그레이션 파일 버전이 커밋 시각보다 과거인 경우 CI 에서 경고하도록 lint 를 도입한다.
  • capture-intelligence-agent 가 요약 write 실패 시 summary_state=error 로 결과를 폐기하기 전에, 서버측 5xx/스키마성 오류와 진짜 client 입력 오류를 구분해 재시도 여부를 결정하도록 한다 (현재는 4xx 를 모두 삭제 대상으로 처리).

Monitoring#

drift 재발 또는 미해소를 추적하는 로그 검색 쿼리다. 수정 배포 후 0 으로 떨어져야 한다.

text
service:cupixworks-api status:error "spacetimes.summary_text"

agent 측 요약 write 실패 관측:

text
service:cupixworks-capture-intelligence-agent "Unknown column 'spacetimes.summary_text'"

update_spacetime 400 전반 (요약 저장 경로 건강도):

text
service:cupixworks-api @http.status_code:400 "update_spacetime"

Risk Assessment#

  • Risk level: medium (production 사용자 대상 AI 요약 저장 실패, 데이터 손실은 없으나 요약 결과 폐기)
  • 예상 복잡도: trivial (기존 idempotent 마이그레이션 병합/배포)