Странная ошибка миграции в тестах во время GH workflow

Я пытаюсь создать связь внешнего ключа с таблицей Topics.

Проблема в том, что тестовое окружение GitHub Workflow падает по самой странной причине: оно пытается обратиться к полю родительской таблицы, которое больше не существует и было удалено в одном из основных миграций несколько лет назад!

Ошибка: 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

Ещё более странная часть в том, что эта миграция работает в Production!

Буду признателен за любые подсказки!

О, меня тут хорошенько отлупили по Rubocop :policeman: :police_car:

Offenses:

db/migrate/20231119010101_create_locations_topic_table.rb:6:7: C: Discourse/NoAddReferenceOrAliasesActiveRecordMigration: Методы AR add_reference, add_belongs_to, t.references и t.belongs_to
высокорискованны для больших таблиц и содержат слишком много скрытых операций.
Вместо этого напишите миграцию с disable_ddl_transactions! и используйте
пользовательский SQL для добавления нового столбца и выполнения CREATE INDEX CONCURRENTLY. Используйте предложение IF NOT EXISTS,
чтобы миграцию можно было перезапускать, если она прервётся на полпути.

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

Вот это, возможно, и есть часть проблемы.

Слишком много :magic_wand: — в этом и дело!

Живёшь — и учишься! :mortar_board:

Совет от Rubocop хорош, но я не думаю, что он является причиной этой ошибки. (Упомянутый им «риск» касается блокировок в таблицах с высокой нагрузкой, что влияет на продакшн-трафик. Это не то, что могло бы вызвать ошибки в тестовой среде.)

Вы, вероятно, сможете воспроизвести ту же ошибку локально, выполнив команду RAILS_ENV=test bin/rake db:drop db:create db:migrate (то есть миграцию базы данных с нуля с включённым плагином locations).

Я подозреваю, что проблема в том, что вы вызываете задачи Rake внутри миграции. В ядре Discourse мы обычно избегаем выполнения любого кода приложения внутри миграций из-за возможных странных побочных эффектов. Лучше использовать сырой SQL.

В данном случае я предполагаю, что кэш схемы ActiveRecord заполняется на раннем этапе миграции (когда topics.off_topic_count ещё существует). Затем, когда запускается ваша задача Rake, она работает со старым кэшем схемы, и поэтому ActiveRecord пытается загрузить столбцы, которые больше не существуют.

Вы, вероятно, сможете смягчить проблему, добавив ActiveRecord::Base.clear_cache! перед вызовом задач Rake… но не воспринимайте это как рекомендацию :wink:. Лучшее решение — вообще не вызывать задачи Rake. Если вам нужно манипулировать данными в базе данных, используйте сырой SQL внутри миграции.

Спасибо, Дэвид, я перенесу это в SQL-запрос и посмотрю, решит ли это проблему …

Сделано, спасибо, Дэвид!