Error occurred during comparing location latitude.
RCA: Error occurred during comparing location latitude
Error Log#
Error occurred during comparing location latitude.
Impact#
- Service:
cupixworks-api - 발생 횟수: 2
- 최초 발생: 2026-04-08T22:31:44.083Z
- 최근 발생: 2026-04-08T22:31:44.083Z
Root Cause Summary#
Facility 모델의 _location_field_changed? 메서드에서 saved_changes['sys']로부터 가져온 latitude/longitude 값이 Float가 아닌 String 타입으로 저장되어 있어 - (뺄셈) 연산 시 undefined method '-' for an instance of String 에러가 발생했습니다. sys 컬럼은 Cupix::Util::FlexibleHash coder를 통해 YAML serialize/deserialize되는데, deserialize 과정에서 숫자 값이 String으로 복원되는 경우가 있어 타입 불일치가 발생합니다. 동일 요청에서 latitude 2건, longitude 2건 총 4건의 에러가 발생했으며, rescue 블록에서 catch되어 서비스 장애로 이어지지는 않았지만 timezone 업데이트 로직이 실행되지 않았습니다.
Technical Analysis#
Code Path#
- Entry point:
app/models/concerns/geo_queriable/facility.rb:8—before_commit :update_timezone콜백이 Facility 업데이트 시 트리거됩니다.
# app/models/concerns/geo_queriable/facility.rb:8-17
before_commit :update_timezone, on: :update
private
def update_timezone
return unless location_changed?
Cupix::Logger.info("update timezone on facility #{self.key}.", class: self.class.name, function: __method__, facility: { id: self.id, key: self.key, saved_changes: saved_changes })
self.set_location_based_timezone
end
location_changed?호출:app/models/concerns/geo_queriable.rb:57-62—saved_changes에sys키가 있으면 latitude와 longitude 각각에 대해_location_field_changed?를 호출합니다.
# app/models/concerns/geo_queriable.rb:57-62
def location_changed?
return false if saved_changes.blank?
return false unless saved_changes.keys.include?('sys')
_location_field_changed?('latitude') || _location_field_changed?('longitude')
end
- Failure point:
app/models/concerns/geo_queriable.rb:68-70—saved_changes['sys']에서 가져온before/after값이 String 타입이므로before - after뺄셈 연산에서NoMethodError: undefined method '-' for an instance of String이 발생합니다.
# app/models/concerns/geo_queriable.rb:66-75
def _location_field_changed?(field)
begin
before = saved_changes['sys'][0].try(:[], field) || 0
after = saved_changes['sys'][1].try(:[], field) || 0
(before - after).abs > LOCATION_COMPARE_THRESHOLD
rescue StandardError => e
Cupix::Logger.error("Error occurred during comparing location #{field}.", class: self.class.name, function: __method__, error: e, before: before, after: after)
false
end
end
sys필드는Metableconcern을 통해Cupix::Util::FlexibleHashcoder로 serialize됩니다 (app/models/concerns/metable.rb:8). YAML 기반 serialize/deserialize 과정에서 숫자 값이 String으로 복원될 수 있습니다.
기대 동작: saved_changes['sys'][0]['latitude']가 Float (예: 26.23479)로 반환되어 뺄셈 연산이 정상 수행됨.
실제 동작: String (예: "26.23479")으로 반환되어 - 메서드가 없어 NoMethodError 발생. rescue 블록에서 catch되어 false 반환 → timezone 업데이트가 실행되지 않음.
Log Evidence#
Datadog 검색 쿼리:
service:cupixworks-api status:error "comparing location" @environment:production
4건의 에러 로그가 동일 시각(2026-04-08T22:31:44.083Z), 동일 요청에서 발생:
{
"message": "Error occurred during comparing location latitude.",
"class": "Facility",
"function": "_location_field_changed?",
"error": "undefined method '-' for an instance of String",
"before": "26.23479",
"after": "26.23479",
"request_id": "2c5aec8d-3b3a-4481-9399-756beb167b69",
"tenant": "cupix",
"host": "ip-10-1-80-134.us-west-2.compute.internal",
"pid": 1771631
}
{
"message": "Error occurred during comparing location longitude.",
"class": "Facility",
"function": "_location_field_changed?",
"error": "undefined method '-' for an instance of String",
"before": "-90.0",
"after": "-90.0",
"request_id": "2c5aec8d-3b3a-4481-9399-756beb167b69"
}
타임라인:
2026-04-08T22:31:44.083Z— 단일 요청(2c5aec8d)에서 Facility 업데이트 트리거- latitude 비교 실패 x2, longitude 비교 실패 x2 (총 4건)
before와after값이 동일하므로 실제로 위치가 변경되지 않은 상태였음- 해당 시간대에 이 에러 외 다른 error 레벨 로그는 없었음 (단발성 사건)
배포 버전: production-us-west-2-20260407T0455Z0-045f9011-cupixworks
Fix Recommendation#
즉시 조치 (Critical)#
- 파일:
app/models/concerns/geo_queriable.rb:68-69 saved_changes에서 가져온before/after값에.to_f를 적용하여 String → Float 변환을 보장해야 합니다.- 참고: 이 수정은 이미 commit
0988cc01f(2026-04-09, TSLA-12402)에서 적용되었습니다.
- before = saved_changes['sys'][0].try(:[], field) || 0- after = saved_changes['sys'][1].try(:[], field) || 0+ before = (saved_changes['sys'][0].try(:[], field) || 0).to_f+ after = (saved_changes['sys'][1].try(:[], field) || 0).to_f단기 개선 (1주 이내)#
FlexibleHashcoder의 deserialize 로직을 확인하여 숫자 값이 String으로 복원되는 원인을 파악하고, 가능하면 deserialize 단계에서 타입을 보존하도록 개선해야 합니다.latitude=/longitude=setter에서 이미 Float 검증을 수행하고 있으므로, DB에 저장되기 전 타입이 보장되어야 하지만saved_changes에서 YAML deserialized 값이 다시 String이 되는 경로를 차단해야 합니다.
장기 개선 (재발 방지)#
sys컬럼의 serialize 방식을 YAML 기반FlexibleHash에서 native JSONB 타입으로 마이그레이션하면 타입 보존 문제를 근본적으로 해결할 수 있습니다.GeoQueriable을 사용하는 모든 모델(Capture, Facility, Annotation, Pano, Storage, Team, Workspace)에 대해 location 비교 로직의 통합 테스트를 추가해야 합니다.
Monitoring#
- 동일 에러 재발 모니터링:
service:cupixworks-api status:error "comparing location" @environment:production
.to_f수정 배포 후 에러 발생 여부 확인 (commit0988cc01f반영 확인)FlexibleHashdeserialize에서 타입 불일치가 발생하는 다른 필드가 있는지 확인:
service:cupixworks-api status:error "undefined method" "instance of String" @environment:production
Risk Assessment#
- Risk level: low
- 예상 복잡도: trivial
rescue블록에서 catch되어 서비스 장애로 이어지지 않으며, 영향은 timezone 업데이트 누락에 한정됩니다. 수정이 이미 적용되어 있습니다 (commit0988cc01f).