ES /docs

Error occurred during comparing location latitude.

RCA: Error occurred during comparing location latitude

Error Log#

Datadog Logs

text
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:8before_commit :update_timezone 콜백이 Facility 업데이트 시 트리거됩니다.
ruby
# 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-62saved_changessys 키가 있으면 latitude와 longitude 각각에 대해 _location_field_changed?를 호출합니다.
ruby
# 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-70saved_changes['sys']에서 가져온 before/after 값이 String 타입이므로 before - after 뺄셈 연산에서 NoMethodError: undefined method '-' for an instance of String이 발생합니다.
ruby
# 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 필드는 Metable concern을 통해 Cupix::Util::FlexibleHash coder로 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 검색 쿼리:

text
service:cupixworks-api status:error "comparing location" @environment:production

4건의 에러 로그가 동일 시각(2026-04-08T22:31:44.083Z), 동일 요청에서 발생:

json
{
  "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
}
json
{
  "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건)
  • beforeafter 값이 동일하므로 실제로 위치가 변경되지 않은 상태였음
  • 해당 시간대에 이 에러 외 다른 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주 이내)#

  • FlexibleHash coder의 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#

  • 동일 에러 재발 모니터링:
text
service:cupixworks-api status:error "comparing location" @environment:production
  • .to_f 수정 배포 후 에러 발생 여부 확인 (commit 0988cc01f 반영 확인)
  • FlexibleHash deserialize에서 타입 불일치가 발생하는 다른 필드가 있는지 확인:
text
service:cupixworks-api status:error "undefined method" "instance of String" @environment:production

Risk Assessment#

  • Risk level: low
  • 예상 복잡도: trivial
  • rescue 블록에서 catch되어 서비스 장애로 이어지지 않으며, 영향은 timezone 업데이트 누락에 한정됩니다. 수정이 이미 적용되어 있습니다 (commit 0988cc01f).