Database import retries exhausted: migration id(1843) - ActiveRecord::RecordNotFound: Couldn't find
Fix Plan: ImportWorker cannot find newly-copied Pano because state=abandoned is excluded by default_scope
Summary#
RCA "즉시 조치 (Critical)" 권고에 따라 두 가지 방어를 함께 적용합니다.
- Option A (근본 원인):
migrate_panos진입 직후pano_data['pano']에서state == 'abandoned'인 항목을 걸러내 새 facility 로 복사되지 않도록 한다.pano_resource,mask,migrate_tile_object,migrate_mask_object,set_pano_states_done모두 이 필터링된 원본 id 집합만 다루게 되어 정합성이 유지된다. - Option B (방어): 이후
Pano.find(new_id)호출 3곳 (라인 708, 755, 793) 을Pano.unscoped.find(new_id)로 변경해default_scope { where.not(state: :abandoned) }에 걸리지 않도록 한다. Option A 로 정상 데이터에서는 abandoned 가 사라지지만, 향후 다른 경로로 abandoned row 가 복사되더라도RecordNotFound로 migration 전체가 중단되지 않도록 하기 위한 방어 코드다.
Changes#
tesla: app/operations/migration_import_operation.rb#
-
What:
migrate_panos진입 지점에서pano_data['pano']를 non-destructive 하게 필터링해state == 'abandoned'인 원본 pano 를 migration 대상에서 제외한다. 걸러진 pano 수를Cupix::Logger.info로 남긴다. 필터링된 데이터를 로컬 변수(filtered_pano_data) 로 만들어 이후 모든 하위 호출(migrate_model('pano', ...),migrate_resource_object,migrate_tile_object,migrate_mask_object,set_pano_states_done) 에 그대로 전달한다. -
Why: RCA Root Cause — 원본이 abandoned 인 pano 는
insert_model!이 state 를 그대로 복사해 새 Pano 도 abandoned 로 저장되고, 이어지는Pano.find(new_id)가Statable::Pano의 default_scope 에 걸려RecordNotFound를 던진다. 비즈니스 관점에서도 abandoned 는 "무시된" 상태이므로 새 facility 로 복사할 대상이 아니다. -
Lines:
migrate_panos함수 (line 310-347) 진입 부분에 5-10 라인 추가. -
What: 같은 파일의
migrate_tile_object(line 708),migrate_mask_object(line 755),set_pano_states_done(line 793) 세 지점의Pano.find(new_id)(또는model_name.camelize.constantize.find(new_id)) 호출을unscoped.find(new_id)를 사용하도록 변경한다. -
Why: RCA Option B — 방어 코드. Option A 필터로 abandoned 는 걸러지지만, migration 이후 재시도/부분 실패 등으로 새 Pano row 의 state 가 일시적으로 abandoned 가 되는 경로가 남을 수 있다.
unscoped로 조회하면 default_scope 를 우회해 결정론적 실패를 방지한다. -
Lines: 708, 755, 793 (각 1라인 수정).
Acceptance Criteria#
-
migrate_panos는pano_data['pano']원본 hash 에서data['state'] == 'abandoned'인 항목을 걸러낸 뒤 하위 함수를 호출한다. - 걸러낸 pano 수와 원본 id 를 info 로그로 기록한다 (
Cupix::Logger.info,class/method메타 포함, 기존 로그 스타일 준수). -
grep -n "\\.find(new_id)" app/operations/migration_import_operation.rb결과가 모두unscoped.find(new_id)형태이거나 다른 안전한 조회 방식으로 대체되어야 한다 (세 지점 모두). - 원본 pano_data 인자는 mutate 하지 않는다 (호출자가 pano_data 를 이후 다른 목적으로 참조할 수 있음).
- 기존 동작 유지: abandoned 가 아닌 pano 들에 대해서는 이전과 동일하게
migrate_model,migrate_resource_object,migrate_tile_object,migrate_mask_object,set_pano_states_done흐름이 실행된다. -
bundle exec rubocop app/operations/migration_import_operation.rb통과. -
bundle exec srb tc app/operations/migration_import_operation.rb통과.
Tests#
- 기존 테스트:
spec/operations/migration_import_operation_spec.rb— 존재하면 통과해야 함 (migrate_panos를 커버하는 example 이 있으면 abandoned 필터링 시나리오도 함께 확인). - 신규 테스트: 없음. RCA 는 데이터 이상 상황의 방어 코드이며, 기존 테스트가 없거나 커버리지가 얕은 상황이므로, 최소 변경 원칙을 준수해 이번 PR 에서 신규 spec 은 추가하지 않는다. (필요 시 후속 이슈로 분리)