Плагин вызывает ошибки при пересборке

Сегодня я попытался обновить установку Discourse с версии 2.9.0.beta9 до 2.9.0.beta10, но всё пошло ужасно не так.

  1. Моя первая попытка была через консоль:
cd /var/discourse
sudo git pull
sudo ./launcher rebuild app

В итоге процесс прервался, и где-то посередине логов появилось следующее сообщение:

PG::InvalidParameterValue: ERROR:  cannot extract elements from a scalar
/var/www/discourse/vendor/bundle/ruby/2.7.0/gems/rack-mini-profiler-3.0.0/lib/patches/db/pg.rb:110:in `exec'

Мне удалось просто восстановить форум, запустив старый образ (который всё ещё оставался на месте).

  1. Моя вторая попытка была через веб-консоль (/admin/upgrade). Но в итоге она также завершилась ошибкой того же типа: ERROR: cannot extract elements from a scalar. При возврате на панель обновления система сообщила, что все компоненты успешно обновлены. Однако после перезапуска контейнера сервер стал выдавать ошибку HTTP 500 :frowning_face:

  2. Я выполнил чистую установку на отдельной машине, начиная с нуля. Мне удалось установить версию beta10, но попытка восстановления из резервной копии вызвала точно такую же ошибку!

Есть ли кто-нибудь, кто мог бы помочь?

Я нашел запрос, который вызывает проблему:

INSERT INTO question_answer_votes (post_id, user_id, created_at)
	SELECT
	  X.post_id AS post_id,
	  (X.value->>'user_id')::int AS user_id,
	  (X.value->>'created_at')::timestamp AS created_at
	FROM (
	  SELECT
	    post_id,
	    jsonb_array_elements(value::jsonb) AS value
	  FROM post_custom_fields WHERE name = 'vote_history'
	) AS X
	WHERE (X.value->>'action') != 'destroy'
	ORDER BY (X.value->>'created_at')::timestamp DESC
	ON CONFLICT DO NOTHING

Если я правильно понимаю, это происходит потому, что функция psql jsonb_array_elements ожидает массив, но получает значение NULL.

Появилось несколько сообщений об этом:

Думаю, это может быть связано с тем, что ранее был установлен плагин Pavilion Question/Answer?

Попробую найти кого-нибудь, кто сможет провести более глубокое исследование. :+1:

Мы используем этот плагин: https://github.com/angusmcleod/discourse-question-answer.

  • Похоже, он связан с плагином Pavilion Question and Answer.
  • Это форк плагина discourse/discourse-upvotes.

Просто для уточнения: какие плагины у вас указаны в app.yml?

На сайте, который я видел, было множество пользовательских полей постов, содержащих строки, закодированные многократно, из-за чего они стали непригодными для использования.

Вот несколько решений, отсортированных по сложности:

  • отказаться от использования плагина (и, возможно, переключиться на новую систему голосований);
  • удалить все такие поля;
  • удалить только проблемные поля;
  • отредактировать эти поля, чтобы преобразовать данные обратно в валидные JSON-строки.

На том сайте, который я видел, были некорректные данные, которым уже несколько лет. Похоже, это баг, существовавший много лет назад, но обнаруженный только сейчас.

Возможно, написать код для исправления повреждённых JSON-строк, но за 10 минут мне не удалось понять, как это сделать.

Смотрите примеры по ссылке: Question Answer Plugin - #301 by pfaffman

Я тоже об этом подумал, но после быстрой проверки форка Ангуса выяснилось, что миграции данных там, похоже, не проводились. https://github.com/angusmcleod/discourse-question-answer/compare/main...discourse:discourse-upvotes:main

Как вы и сказали, это похоже на загрязнение данных из старых постов Q&A. Мне также забавно, что это всплыло именно сейчас, учитывая, что миграция была проведена ещё в ноябре 2021 года. Думаю, это потому, что JayJay только сейчас переключается на новый плагин? В любом случае мы ещё раз изучим эту миграцию.

          - git clone https://github.com/discourse/docker_manager.git
          - git clone https://github.com/discourse/discourse-solved.git
          - git clone https://github.com/unfoldingWord-dev/discourse-mermaid.git
          - git clone https://github.com/angusmcleod/discourse-question-answer.git
          - git clone https://github.com/discourse/discourse-checklist.git
          - git clone https://github.com/discourse/discourse-cakeday.git
          - git clone https://github.com/discourse/discourse-canned-replies.git
          - git clone https://github.com/discourse/discourse-footnote.git
          - git clone https://github.com/discourse/discourse-staff-notes.git
          - git clone https://github.com/discourse/discourse-graphviz.git
          - git clone https://github.com/discourse/discourse-assign.git
          - git clone https://github.com/discourse/discourse-voting.git
          - git clone https://github.com/discourse/discourse-yearly-review.git
          - git clone https://github.com/discourse/discourse-saved-searches.git

@nat Я вообще не перехожу на новый плагин. Я всё ещё работаю с набором плагинов, который у нас уже довольно давно.

Как минимум раз в месяц мы выполняем обновления, так что огромного разрыва между версиями нет.

Просто как небольшая справка, помимо вопроса об ответах, теперь также есть замена canned-replies:

И, по-моему, теперь также существует официальный official компонент темы Mermaid:

Спасибо. Разберусь с этим, как только будет решена моя первоначальная проблема :smile:

Если я удалю плагин, восстановление резервной копии всё равно запустит проблемный запрос.
Поэтому мне нужно будет удалить как сам плагин, так и любые таблицы и запросы, которые он использует.
Как мне узнать, какие таблицы задействованы?

Вы закомментировали строку для плагина и попытались выполнить повторную сборку, чтобы убедиться в этом?

Я сейчас попробую. Я просмотрел файл dump.sql, который входит в резервную копию, и там вообще нет упоминания какой-либо таблицы question_answer_*. Это вселяет в меня надежду…

Мне удалось добиться успеха в моей тестовой системе!

  • Отключил плагин
  • sudo ./launcher rebuild app
  • Восстановил резервную копию

Сейчас займусь рабочей системой. Буду держать вас в курсе.

Повреждённые данные находятся в таблице PostCustmField. Однако, если у вас нет плагина, система не попытается перенести эти данные в новую таблицу.

Проблема не в самой миграции, а в том, что в какой-то момент в прошлом данные были повреждены. Они были неработоспособны, но эта неисправность оставалась незамеченной в течение многих лет. Возможно, каждое новое голосование «за» усугубляло повреждение. Разумным решением может быть игнорирование повреждённых данных, возможно, пометив их как повреждённые (например, переименовав пользовательское поле), чтобы будущие миграции могли их игнорировать, а при желании кто-то мог вручную исправить их.

Успех и в режиме live. Отключил плагин, и мы продолжили работу.
Всё ещё удивляюсь, почему я не столкнулся с этой проблемой раньше. Как я уже говорил, каждый месяц мы в рамках рутинных процедур обновляем все наши системы, поэтому я должен был заметить эту проблему раньше.

Как я могу подключиться к базе данных Discourse самостоятельно, чтобы проверить содержимое таблицы post_custom_fields?

@Jaap-Jan_Swijnenburg Мне жаль, что у вас возникла проблема.

Я вынужден с вами не согласиться :slight_smile:

Старый плагин QnA использовал стандартную систему приведения типов для пользовательских полей Discourse, предназначенную для преобразования этого поля в JSON. Однако в некоторых случаях (как в данном) это не работает, поэтому (в идеале) необходимо добавлять проверки формата при работе с пользовательскими полями Discourse (особенно если вы имеете дело с довольно старым кодом и данными, как здесь). Я полагаю, что именно это должен сделать плагин «Голоса» (Upvotes Plugin) в данном случае, что и является причиной непосредственной ошибки.

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

Что ж, в основном я полагаюсь на ваше мнение по этому поводу, и мне кажется, что мы во многом согласны. Я считаю, что миграция работает, если данные не повреждены. Мы согласны с тем, что при повреждении данных процесс должен завершаться более корректно, чем это происходит сейчас.

Да, но данные, скорее всего, были повреждены ещё годы назад (это случай с сайтом, который мне знаком), но вы этого не заметили, потому что не произошло катастрофического сбоя. Я почти уверен, что система не управляла голосами «за» так, как ожидалось, но никто этого не заметил.

Я бы сделал это через Rails, примерно так:

./launcher enter app
rails c

Затем что-то вроде этого:

all_votes=PostCustomField.where(name: "vote_history")
likely_broken_votes=PostCustomField.where(name: "vote_history").where("value like '\"%'")

Посмотрите только на id и данные:

all_votes.pluck(:id,:value)

Получите одну запись pcf:

pcf=PostCustomField.find(1234)

Исправьте её:

pcf.value='то, что вы действительно хотите там видеть'
pcf.save

Вот что происходит:

  1. У некоторых людей в файле app.yml указана очень старая версия плагина «Вопросы и ответы» с моим личным именем пользователя GitHub в URL репозитория.

  2. Я передал плагин QnA в paviliondev несколько лет назад. GitHub перенаправляет URL репозиториев при их передаче, поэтому старые URL с моим личным именем пользователя продолжали работать.

  3. Спустя ещё несколько лет Pavilion передал плагин «Вопросы и ответы» в Discourse. Изначально Discourse сохранил название discourse-question-answer.

  4. Несколько месяцев назад я создал свой собственный форк плагина, размещённый в discourse, пока он всё ещё назывался discourse-question-answer.

  5. Люди со старыми ссылками на плагин QnA, содержащими мой личный аккаунт GitHub, теперь клонировали мой новый форк значительно обновлённого плагина discourse-question-answer, размещённого в discourse. Иными словами, очень старая ссылка теперь указывала на форк нового плагина. Несмотря на прошедшие годы, я должен был предвидеть это, поэтому приношу извинения. Я удалил этот форк.

  6. Discourse изменил название discourse-question-answer на discourse-upvotes. Это изменение названия не оказало существенного влияния на ваш случай, @Jaap-Jan_Swijnenburg, но именно поэтому вы теперь (неожиданно) клонируете форк этого репозитория.

  7. Миграция в discourse/discourse-upvotes (ранее discourse-question-answer) предполагает, что столбец value в post_custom_fields является типобезопасным JSON.

  8. Старые данные, созданные плагином, когда он назывался angusmcleod/discourse-question-answer (несколько лет назад), не сохранялись как валидный JSON благодаря concern HasCustomFields в discourse/discourse. Я предполагаю, что эти данные были добавлены до того, как 4 года назад было добавлено проверка типов JSON (в крайних случаях всё ещё возможно появление невалидного JSON в пользовательских полях, зарегистрированных как JSON).

Следовательно:

  1. Когда люди со (устаревшей на несколько лет) ссылкой angusmcleod/discourse-question-answer в своём файле app.yml обновляют Discourse, миграция из новой версии плагина клонируется, запускается и потенциально вызывает эту ошибку.

Есть несколько решений этой проблемы:

  1. @Jaap-Jan_Swijnenburg, в вашем случае вам просто нужно удалить ссылку на мой старый плагин QnA, и вы сможете пересобрать свой сайт. Всё, больше ничего. Похоже, вы уже это сделали :+1:

  2. Миграцию плагина discourse/discourse-upvotes можно обновить, чтобы она обрабатывала значения, не являющиеся JSON, в столбце value таблицы post_custom_fields.

Отмечу, что вариант 2 также покроет дополнительный случай, когда люди действительно хотят перейти со старой версии плагина QnA на discourse-upvotes. В этом случае миграция запустится, но завершится ошибкой, если какие-либо записи в столбце value таблицы post_custom_fields не являются валидным JSON.