ES /docs

ActiveModel::MissingAttributeError: missing attribute 'origin_x' for Building

RCA: ActiveModel::MissingAttributeError: missing attribute 'origin_x' for Building

Overview#

What Happened#

2026-08-06 롤링 배포 진행 중(production, us-west-2) GET /api/v1/buildings/{id} (Api::V1::BuildingsController#show) 요청이 약 30분 동안 HTTP 500 을 반환했다. BuildingSerializerrecord.origin_x 를 읽는 순간, 해당 요청을 처리한 일부 앱 인스턴스의 Building 모델이 origin_x 컬럼을 인식하지 못해 ActiveModel::MissingAttributeError 또는 NoMethodError 가 발생했다. 배포가 끝난 뒤(14:42 KST 이후) 에러는 저절로 사라졌고 #index 요청은 정상 200 을 반환했다.

Quick Facts#

Field Value
exception.class ActiveModel::MissingAttributeError (+ 동시 발생 NoMethodError)
exception.message missing attribute 'origin_x' for Building / undefined method 'origin_x' for an instance of Building
top_frame app/serializers/building_serializer.rb:16
endpoint GET /api/v1/buildings/{id} (Api::V1::BuildingsController#show)
deploy version:production-us-west-2-20260806t0214z... / t0341z... / t0525z... (SHA c1c2bbd4, 롤링 중)
env production, us-west-2

Affected Teams#

Team / Domain Error Count Impact
production us-west-2 (buildings 48/58/59) 14 (ET occurrence) / 21 (log line, 중복 포함) 배포 창(window) 동안 특정 building 상세 조회 500, 배포 완료 후 자동 회복

Timeline#

  1. 2026-08-06 14:11 KSTGET /api/v1/buildings/59 부터 500 급증 시작 (배포 창 진입)
  2. 2026-08-06 14:11–14:42 KST — buildings 48/58/59 에 대해 MissingAttributeErrorNoMethodError 가 번갈아 발생 (ET first_seen 05:11:51Z ~ last_seen 05:42:15Z UTC)
  3. 2026-08-06 14:42 KST — 마지막 500 발생 (GET /api/v1/buildings/48)
  4. 2026-08-06 14:56 KST 이후GET /api/v1/buildings (#index) 정상 200 반환, 500 재발 없음 → 배포 완료로 자연 회복

Error Log#

Datadog Logs

text
missing attribute 'origin_x' for Building

Impact#

  • Service: cupixworks-api
  • 발생 횟수: 14
  • 최초 발생: 2026-08-06 14:11 KST
  • 최근 발생: 2026-08-06 14:42 KST

Root Cause Summary#

이 에러는 Building 데이터 결함이나 BuildingSerializer 코드 결함이 아니라, 롤링 배포 창 동안 발생한 schema/attribute 인식 불일치(transient) 이다. origin_x 컬럼은 create_buildings 마이그레이션(db/migrate/20260206002112_create_buildings.rb:16)의 일부로 이미 production buildings 테이블에 존재하고, db/schema.rb:916 에도 반영되어 있다. 그러나 2026-08-06 배포가 롤링으로 진행되는 동안(같은 SHA c1c2bbd4, version 태그 timestamp 가 0214z → 0341z → 0525z 로 여러 개) 요청을 처리한 일부 앱 인스턴스의 Building 모델이 origin_x 를 알지 못하는 상태였다. Rails 는 부팅 시 실제 DB 컬럼으로부터 attribute 접근 메서드를 생성하므로, 컬럼 인식이 되지 않은 인스턴스에서는 record.origin_x 호출이 NoMethodError: undefined method 'origin_x'(메서드 자체가 정의되지 않음) 로, 쿼리 결과에 컬럼이 없는 경우에는 ActiveModel::MissingAttributeError: missing attribute 'origin_x' 로 표면화된다. 동일 시각에 두 예외가 번갈아 나온 것과, 배포 완료 후 즉시 재발이 멈춘 점이 이 진단을 뒷받침한다. BuildingSerializer 는 정상적으로 컬럼을 읽는 코드이며 수정이 필요 없다.

Technical Analysis#

Code Path#

  • Entry point: app/controllers/api/v1/buildings_controller.rb:39-41set_buildingrepository_instance.show(params[:id])
app/controllers/api/v1/buildings_controller.rb:39-41ruby
def set_building
  @model = repository_instance.show(params[:id])
end
  • 조회 쿼리: app/repositories/base_repository.rb:337permission_joins(default_joins(current_class), current_user).where(attrs). BuildingRepository.default_joinsbuildings.* 를 select 하므로(app/repositories/building_repository.rb:29-31) 정상 상태에서는 origin_x 를 포함한 전체 컬럼이 로드된다.
app/repositories/building_repository.rb:29-31ruby
def self.default_joins(record)
  record.joins(:team).select('buildings.*')
end
  • Failure point: app/serializers/building_serializer.rb:15-17 — 직렬화 단계에서 record.origin_x 를 읽는 순간 예외 발생. 컬럼을 인식하지 못하는 인스턴스에서는 이 호출이 NoMethodError(attribute 메서드 미정의) 또는 MissingAttributeError(결과셋에 컬럼 없음) 로 이어진다.
app/serializers/building_serializer.rb:15-17ruby
attribute :origin_x do |record|
  record.origin_x&.to_f
end
  • 참고: origin_x 는 신규 도입 컬럼이 아니라 buildings 테이블 생성 시점부터 정의된 좌표 컬럼이다.
db/migrate/20260206002112_create_buildings.rb:15-19ruby
# Coordinates for site-level placement
t.decimal :origin_x, precision: 20, scale: 10
t.decimal :origin_y, precision: 20, scale: 10
t.decimal :origin_z, precision: 20, scale: 10
t.decimal :bearing, precision: 10, scale: 6
  • 기대 동작: 배포된 모든 인스턴스가 origin_x 를 포함한 buildings schema 를 인식 → serializer 가 정상적으로 값을 직렬화.
  • 실제 동작: 롤링 배포 창 동안 일부 인스턴스가 origin_x 를 인식하지 못해 serializer 호출 시 예외 → HTTP 500.

Log Evidence#

Datadog 쿼리 (재현 가능):

text
service:cupixworks-api "buildings" "[500]"

핵심 로그 — 동일 시각에 MissingAttributeErrorNoMethodError 가 함께 발생(같은 building 48):

json
{
  "timestamp": "2026-08-06 14:42:15",
  "status": "info",
  "message": "[500] GET /api/v1/buildings/48 (Api::V1::BuildingsController#show)",
  "error": ["ActiveModel::MissingAttributeError", "missing attribute 'origin_x' for Building"]
}
json
{
  "timestamp": "2026-08-06 14:42:14",
  "status": "info",
  "message": "[500] GET /api/v1/buildings/48 (Api::V1::BuildingsController#show)",
  "error": ["NoMethodError", "undefined method `origin_x' for an instance of Building"]
}

배포가 롤링으로 진행 중이었음을 보여주는 version 태그(같은 SHA c1c2bbd4, timestamp 상이):

text
env:production
environment:production
version:production-us-west-2-20260806t0214z0-c1c2bbd4-cupixworks
version:production-us-west-2-20260806t0341z0-c1c2bbd4-cupixworks
version:production-us-west-2-20260806t0525z0-c1c2bbd4-cupixworks

발생 타임라인(500 로그 라인 count, KST):

text
14:11:53–14:11:56  buildings/59
14:12:27–14:12:30  buildings/59
14:22:24–14:22:26  buildings/59
14:35:14–14:35:18  buildings/58
14:42:14–14:42:15  buildings/48
(이후 재발 없음)

배포 완료 후 정상화 확인:

text
service:cupixworks-api "GET /api/v1/buildings" "[200]"
json
{
  "timestamp": "2026-08-06 14:58:02",
  "status": "info",
  "message": "[200] GET /api/v1/buildings (Api::V1::BuildingsController#index)"
}

Hypotheses Considered#

# Hypothesis Evidence for Evidence against Verdict
H1 롤링 배포 창 동안 일부 인스턴스가 origin_x 컬럼을 인식하지 못한 transient schema/attribute 불일치 에러가 배포 창(14:11–14:42 KST)에만 집중, 완료 후 재발 0; version 태그가 같은 SHA 로 여러 timestamp(0214z/0341z/0525z) = 롤링 진행 중; 동일 시각에 MissingAttributeError(결과셋 컬럼 없음)와 NoMethodError(attribute 메서드 미정의)가 공존 = schema 인식 문제의 전형 Confirmed
H2 BuildingSerializer 코드 결함 (잘못된 attribute 참조) serializer 가 origin_x 를 직접 참조 origin_xcreate_buildings 마이그레이션 및 db/schema.rb:916 에 정상 존재; 배포 완료 후 동일 코드로 200 정상 반환 → 코드 결함 아님 Rejected
H3 특정 building 레코드(48/58/59) 데이터 손상 소수 building ID 에 집중 서로 다른 3개 building 에 걸쳐 발생하고, 배포 완료 후 같은 레코드가 정상 조회됨 → 레코드 단위 손상 아님 Rejected
H4 origin_x 컬럼이 production DB 에 아예 없음(마이그레이션 미실행) NoMethodError: undefined method 'origin_x' 컬럼은 buildings 생성 마이그레이션의 일부(2026-02)이며 이미 배포됨; 에러가 영구적이지 않고 배포 완료와 함께 즉시 소멸 → 컬럼 부재가 아니라 인스턴스별 인식 지연 Rejected
H5 permission_joinsGROUP BY id + 축약 select 로 origin_x 미로딩 permission_joins.group('id').select(...) 수행 default_joinsbuildings.* 를 먼저 select 하고 AR .select 는 누적되므로 정상 상태에선 origin_x 포함; 상시 발생이 아니라 배포 창 한정 → 쿼리 구조 문제 아님 Rejected

Fix Recommendation#

즉시 조치 (Critical)#

  • 코드 변경 불필요. BuildingSerializerBuildingRepository 는 정상이며 origin_x 컬럼도 이미 존재한다. 에러는 배포 완료와 함께 이미 해소되었다(14:42 KST 이후 재발 0). Error Tracking 이슈는 ignore 처리 권장.

단기 개선 (1주 이내)#

  • 배포 절차 점검: DB 마이그레이션과 코드 롤아웃의 순서/타이밍을 확인한다. .platform/hooks/ 에는 DB 마이그레이션 단계가 없고(postdeploy/50_init_tesla.sh:11es:migrate 뿐) 마이그레이션이 배포와 분리되어 실행되므로, 롤링 중 신구 인스턴스가 서로 다른 schema 인식 상태로 공존할 수 있다. 배포 시 신규 인스턴스가 트래픽을 받기 전에 schema 를 확실히 인식하도록(warmup 또는 connection schema reload) 절차를 보강한다.
  • 재발 시 빠른 판별을 위해 배포 window 와 에러 급증의 상관관계를 대시보드에 노출한다(아래 Monitoring).

장기 개선 (재발 방지)#

  • 컬럼 추가/스키마 변경을 동반하는 배포에서 "expand → deploy → contract" 패턴을 적용해 신구 코드가 동일 schema 를 안전하게 공유하도록 한다.
  • 롤링 배포 중 신규 인스턴스가 health check 통과 후에도 attribute/schema 캐시를 강제 reload 하도록 부팅 시퀀스를 검토한다.

Monitoring#

  • buildings 엔드포인트 500 추이(배포 창 상관 확인용):
text
service:cupixworks-api "GET /api/v1/buildings" "[500]"
  • origin_x 인식 실패 예외 발생 여부:
text
service:cupixworks-api "origin_x" ("MissingAttributeError" OR "NoMethodError")

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial (코드 변경 없음, 배포 절차 관점의 운영 개선)

Noise Verdict#

noise — origin_x 컬럼은 이미 존재하고 코드도 정상이며, 에러는 2026-08-06 롤링 배포 창 동안의 일시적 schema 인식 불일치로 배포 완료와 함께 자동 소멸했으므로 코드 수정이 필요 없는 transient 배포 아티팩트입니다.