GH 워크플로에서 테스트 중 이상한 마이그레이션 오류

Topics 테이블과의 외래키 관계를 생성하려고 합니다.

문제는 가장 이상한 이유로 GitHub 워크플로 테스트 환경에서 테스트 중에 실패한다는 것입니다. 수년 전 코어 마이그레이션에서 이미 제거되었는데, 더 이상 존재하지 않는 부모 테이블의 필드에 접근하려고 시도하고 있습니다!

에러는 PG::UndefinedColumn: ERROR: column topics.off_topic_count does not exist입니다.

글쎄요, 2018년 코어 마이그레이션에서 제거되었기 때문이죠!

이것이 테스트 과정에서 이미 실행되었음을 확인했습니다:

== 20180917024729 RemoveSuperfluousColumns: migrating =========================
== 20180917024729 RemoveSuperfluousColumns: migrated (0.0410s) ================

저는 이 오래된 부모 테이블 필드를 명시적으로 참조하고 있지 않습니다 … SQL이 자체적으로 생성되는 것 같습니다 … 하지만 현재 정의에 대해서는 적절하지 않게 말이죠.

== 20231119010101 CreateLocationsTopicTable: migrating ========================rake aborted!

[12035](https://github.com/paviliondev/discourse-locations/actions/runs/7039607316/job/19158951878?pr=103#step:19:12036)StandardError: An error has occurred, this and all later migrations canceled: (StandardError)

[12036](https://github.com/paviliondev/discourse-locations/actions/runs/7039607316/job/19158951878?pr=103#step:19:12037)

[12037](https://github.com/paviliondev/discourse-locations/actions/runs/7039607316/job/19158951878?pr=103#step:19:12038)PG::UndefinedColumn: ERROR: column topics.off_topic_count does not exist

[12038](https://github.com/paviliondev/discourse-locations/actions/runs/7039607316/job/19158951878?pr=103#step:19:12039)LINE 1: ...cs"."deleted_at", "topics"."highest_post_number", "topics"."...

테이블 정의는 정말로 단순합니다:

class CreateLocationsTopicTable < ActiveRecord::Migration[7.0]
  def change
    create_table :locations_topic do |t|
      t.references :topic, foreign_key: true
      t.float :latitude, null: false

SNIP

더 이상한 부분은 이 마이그레이션이 프로덕션에서는 작동한다는 것입니다!

어떤 통찰도 환영합니다!

오, 이 때문에 좋은 옛날식 RUBOCOP(코드 스타일 검사 도구) 처벌을 받았네요 :policeman: :police_car:

Offenses:

db/migrate/20231119010101_create_locations_topic_table.rb:6:7: C: Discourse/NoAddReferenceOrAliasesActiveRecordMigration: AR methods add_reference, add_belongs_to, t.references, and t.belongs_to are
high-risk for large tables and have too many background magic operations.
Instead, write a disable_ddl_transactions! migration and write custom SQL to
add the new column and CREATE INDEX CONCURRENTLY. Use the IF NOT EXISTS clause
to make the migration re-runnable if it fails partway through.

      t.references :topic, foreign_key: true
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

자, 이것이 문제의 일부일 수도 있겠네요.

너무 많은 :magic_wand: 마법이 일어나는 게 문제입니다!

살다 보면 배울 점이 있죠! :mortar_board:

rubocop의 조언은 좋지만, 이 오류의 원인은 아닐 것 같습니다. (그곳에서 언급된 'risk’는 높은 트래픽을 받는 테이블에 대한 잠금(lock)을 의미하며, 이는 프로덕션 트래픽에 영향을 미칩니다. 따라서 테스트 환경에서 오류를 일으킬 만한 것이 아닙니다)

RAILS_ENV=test bin/rake db:drop db:create db:migrate를 실행하여 로컬에서도 동일한 오류를 재현할 수 있을 것입니다. (즉, locations 플러그인이 활성화된 상태에서 데이터베이스를 처음부터 마이그레이션하는 것입니다)

마이그레이션 안에서 Rake 태스크를 호출하고 있다는 점이 문제일 것으로 의심됩니다. 코어에서는 이상한 부수 효과(side effects)가 발생할 수 있기 때문에 마이그레이션 내에서 어떤 종류의 애플리케이션 코드도 실행하는 것을 일반적으로 피합니다. 순수 SQL을 사용하는 것이 가장 좋습니다.

이 경우, 제 추측은 ActiveRecord 스키마 캐시가 마이그레이션 초기 단계(즉, topics.off_off_count가 아직 존재할 때)에 미리 채워진다는 것입니다. 그런 다음 rake 태스크가 실행되면, 오래된 스키마 캐시를 사용하여 실행되므로 ActiveRecord가 더 이상 존재하지 않는 컬럼을 로드하려고 시도하게 됩니다.

rake 태스크를 호출하기 전에 ActiveRecord::Base.clear_cache!를 추가하면 완화할 수 있을 것입니다… 하지만 그걸 추천으로 받아들이지는 마세요 :wink:. 최선은 rake 태스크 호출 자체를 피하는 것입니다. 데이터베이스에서 무언가를 조작해야 한다면, 마이그레이션 내에서 순수 SQL을 사용하세요.

데이비드, 감사합니다. SQL 문장으로 변경해서 해결되는지 확인해 보겠습니다 …

해결되었습니다, 데이비드 감사합니다!