Ошибка обновления 2.7.0.beta2: ERROR: duplicate key

Я обновлялся с версии 2.7.0.beta1 и получил следующее сообщение об ошибке:

2021-01-22 20:16:22.015 UTC [4055] discourse@discourse LOG:  duration: 75335.241 ms  statement: UPDATE notifications SET processed = true
2021-01-22 20:16:23.792 UTC [4055] discourse@discourse LOG:  duration: 1776.591 ms  statement: ALTER TABLE "notifications" ALTER COLUMN "processed" SET NOT NULL
2021-01-22 20:16:25.198 UTC [4055] discourse@discourse LOG:  duration: 1323.298 ms  statement: CREATE  INDEX  "index_notifications_on_processed" ON "notifications"  ("processed")
2021-01-22 20:16:25.458 UTC [4055] discourse@discourse LOG:  duration: 241.063 ms  statement: CREATE TABLE "user_notification_schedules" ("id" bigserial primary key, "user_id" integer NOT NULL, "enabled" boolean DEFAULT FALSE NOT NULL, "day_0_start_time" integer NOT NULL, "day_0_end_time" integer NOT NULL, "day_1_start_time" integer NOT NULL, "day_1_end_time" integer NOT NULL, "day_2_start_time" integer NOT NULL, "day_2_end_time" integer NOT NULL, "day_3_start_time" integer NOT NULL, "day_3_end_time" integer NOT NULL, "day_4_start_time" integer NOT NULL, "day_4_end_time" integer NOT NULL, "day_5_start_time" integer NOT NULL, "day_5_end_time" integer NOT NULL, "day_6_start_time" integer NOT NULL, "day_6_end_time" integer NOT NULL)
2021-01-22 20:16:25.560 UTC [4055] discourse@discourse LOG:  duration: 100.868 ms  statement: CREATE  INDEX  "index_user_notification_schedules_on_user_id" ON "user_notification_schedules"  ("user_id")
2021-01-22 20:16:25.782 UTC [4055] discourse@discourse LOG:  duration: 142.180 ms  statement: CREATE  INDEX  "index_do_not_disturb_timings_on_scheduled" ON "do_not_disturb_timings"  ("scheduled")
2021-01-22 20:16:26.414 UTC [4055] discourse@discourse LOG:  duration: 361.514 ms  statement: UPDATE users
	SET locale = 'en_GB'
	WHERE locale = 'en'
	
2021-01-22 20:16:26.656 UTC [4055] discourse@discourse LOG:  duration: 132.778 ms  statement: UPDATE theme_translation_overrides
	SET locale = 'en_GB'
	WHERE locale = 'en'
	
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse ERROR:  duplicate key value violates unique constraint "index_users_on_username"
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse DETAIL:  Key (username)=(DaveW) already exists.
2021-01-22 20:16:42.745 UTC [4055] discourse@discourse STATEMENT:  UPDATE users
	SET locale = 'en'
	WHERE locale = 'en_US'
	
rake aborted!
StandardError: Произошла ошибка, и все последующие миграции отменены:

PG::UniqueViolation: ОШИБКА: дубликат ключа нарушает уникальное ограничение "index_users_on_username"
DETAIL:  Ключ (username)=(DaveW) уже существует.

А затем, в конце вывода:

I, [2021-01-22T20:16:42.805286 #1] INFO -- : Завершение асинхронных процессов

I, [2021-01-22T20:16:42.805333 #1] INFO -- : Отправка сигнала INT для HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main pid: 49

I, [2021-01-22T20:16:42.805381 #1] INFO -- : Отправка сигнала TERM для exec chpst -u redis -U redis /usr/bin/redis-server /etc/redis/redis.conf pid: 166

166:signal-handler (1611346602) Получен сигнал SIGTERM, планирование завершения работы...

2021-01-22 20:16:42.805 UTC [49] LOG: получен запрос на быстрое завершение работы

2021-01-22 20:16:42.835 UTC [49] LOG: отмена всех активных транзакций

2021-01-22 20:16:42.857 UTC [49] LOG: фоновый рабочий "logical replication launcher" (PID 58) завершился с кодом выхода 1

166:M 22 Jan 2021 20:16:42.876 # Пользователь запросил завершение работы...

166:M 22 Jan 2021 20:16:42.876 * Сохранение финального снимка RDB перед выходом.

166:M 22 Jan 2021 20:16:44.758 * База данных сохранена на диск

166:M 22 Jan 2021 20:16:44.758 # Redis готов к выходу, до свидания...

2021-01-22 20:16:45.563 UTC [53] LOG: завершение работы

I, [2021-01-22T20:16:52.806177 #1] INFO -- : HOME=/var/lib/postgresql USER=postgres exec chpst -u postgres:postgres:ssl-cert -U postgres:postgres:ssl-cert /usr/lib/postgresql/13/bin/postmaster -D /etc/postgresql/13/main pid:49 не завершился корректно, принудительное завершение!

НЕ УДАЛОСЬ

--------------------

Pups::ExecError: cd /var/www/discourse && su discourse -c 'bundle exec rake db:migrate' завершился с ошибкой, код возврата #<Process::Status: pid 4032 exit 1>

Место возникновения ошибки: /pups/lib/pups/exec_command.rb:112:in `spawn'

Выполнение не удалось с параметрами {"cd"=>"$home", "hook"=>"db_migrate", "cmd"=>["su discourse -c 'bundle exec rake db:migrate'"]}

d627ad17d1f22d839a7dc8099878e6272eb3ea1772539f6628e2a23dd830aca2

** НЕ УДАЛОСЬ ЗАПУСТИТЬСЯ ** пожалуйста, прокрутите вверх и найдите предыдущие сообщения об ошибках, их может быть больше одной.

./discourse-doctor может помочь диагностировать проблему.

Я не понимаю, как могли появиться две учетные записи с одинаковым именем пользователя.

Наш сайт полностью неработоспособен. Что мне делать сейчас?

Спасибо,
Гуннар

Я думаю, что вы сможете восстановить работу сайта, выполнив команду ./launcher start app.

Затем изучите способы удаления дубликата:

и

Дополнительная информация, которую, вероятно, стоит предоставить:

  • Это установка в одном контейнере.
  • Первая пересборка завершилась без ошибок. Эта ошибка возникла при выполнении второй пересборки после успешного завершения первой.

Спасибо,
Гуннар

Хватит ли у вас места на диске? Ошибка связана с необходимым обновлением PostgreSQL до версии 13, насколько мне известно. Следовали ли вы этому методу обновления?

cd /var/discourse
git pull
 ./launcher rebuild app

Если вы поищете эти ошибки на форуме, то увидите много предыдущей информации и возможные решения.

Попробуйте это.

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

Я поднял сайт, переименовав postgres_data_old обратно в postgres_data, а затем выполнив ./launcher start app для запуска старого образа.

Итак, я снова на нуле, но хотя бы сайт работает.

Я попытался найти дубликат ключа, выполнив следующий запрос:

select * from users WHERE username = 'DaveW';

(имя пользователя DaveW было проблемным ключом, как показано в сообщении об ошибке в моём первом посте в этой теме.)

Команда вернула только одну строку — одного пользователя. Что я упускаю?

Спасибо,
Гуннар

У вас их два, возможно, с разным регистром букв в БД:

Вы заходили на мой сайт? :wink:

Я понимаю, что вы имеете в виду: когда я ищу в GUI, я действительно вижу две записи. Однако, когда я, как администратор, кликаю на обе и открываю страницу деталей, они выглядят как один и тот же пользователь. Я не могу найти ни одной разницы между ними. По сути, неважно, на какую из них я нажму — я всегда получаю данные для второй, у которой указано полное имя.

Поскольку этот пользователь никогда не публиковал сообщения и давно не заходил, я удалил аккаунт через GUI.

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

Прямой поиск в базе данных по username = DaveW теперь возвращает ноль строк. Однако, если я выполню запрос:

select * from users WHERE name = 'DaveW';

(именно name, а не username), я получаю одну строку:

 19732 | DaveW    | 2016-11-15 12:43:02.708166 | 2016-11-15 12:43:02.708166 | DaveW |                    0 |                |               |      | t      | davew          | 2016-11-15 12:43:02.708166 | f     | 2017-06-01 18:09:45.018058 |           1 | f        |                |             |                   |              |                |               |     0 |          0 |            | f         |       |                    |        |                  |                         | f      |               |               |                          |                           | 
(1 строка)

Обратите внимание, что в поле username указано DaveW (то же написание)! Этот аккаунт также на три года старше другого.

Могу ли я удалить его командой:

DELETE from users WHERE id = 19732;

без каких-либо негативных последствий?

Спасибо за всю вашу помощь!

Гуннар

Конечно! :grinning:

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

Вы также можете изменить имя пользователя одного из дубликатов.

Верно.

После этого я смогу начать обновление заново? То есть запустить rebuild дважды?

Спасибо!
Гуннар

Это должно сработать. Если только нет других дублирующихся пользователей.

Оказалось, что они есть, по крайней мере один.

PG::UniqueViolation: ОШИБКА: дублирующее значение ключа нарушает уникальное ограничение "index_users_on_username_lower"
ДЕТАЛИ: Ключ (username_lower)=(robs) уже существует.

Есть ли способ просканировать базу данных на наличие дубликатов? Не только пользователей, но и любых таблиц, которые могут вызвать эту ошибку во время обновления?

Я хотел бы найти их и исправить все до повторной попытки.

Также было бы неплохо, если бы это стало частью процесса обновления. Возможно, сначала выполнить сканирование и остановить процесс до внесения каких-либо изменений. Предупредить пользователя и предоставить список дубликатов со ссылкой на страницу здесь, на meta, с инструкциями по исправлению?

Более щадящий подход может заключаться в переиндексации базы данных перед обновлением, что должно указать, какие именно индексы требуют исправления.

Я не специалист по базам данных, поэтому это выглядит пугающе. И если я правильно понял, после этого у вас останутся дубликаты индексов, которые придётся удалять вручную?

Кроме того, вот что странно. Как вы обнаружили на моём сайте с предыдущим дубликатом, я сейчас тоже нашёл в GUI дублирующегося пользователя RobS. Но, как и раньше, независимо от того, на кого из них я нажимал, я всегда попадал на страницу профиля только одного из них. То есть:

  • В GUI найдите список пользователей с именем RobS. Найдите двух. Убедитесь, что у обоих разные статистические данные.
  • Нажмите на пользователя №1. Откроется профиль и статистика, принадлежащие пользователю №2.
  • Нажмите на пользователя №2. Откроется профиль и статистика, принадлежащие пользователю №2.

select * from users WHERE username_lower = 'robs';

Возвращает одну строку: то, что выглядит как пользователь №2 (даты совпадают).

select * from users WHERE username = 'RobS';

Тоже возвращает только одну строку: то, что выглядит как пользователь №1 (снова даты совпадают). У этого пользователя другой ID, чем у другого.

Судя по выводу, у обоих было одинаковое значение username и username_lower, но для каждого оператора SELECT возвращалась только одна строка. Моя база данных в серьёзных проблемах?

Поможет ли это пересоздание индексов?

Я попытался собрать проект снова и обнаружил ещё один дубликат с именем пользователя “drc”. В графическом интерфейсе я нашёл двух пользователей:

Deborah C
David C

Попробуйте нажать на любого из них, чтобы увидеть детали на их «карточке» профиля. Вы заметите, что независимо от того, на кого вы нажмёте, будут отображены данные для Deborah C.

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

Как мне выполнить повторную индексацию? Например, так?

REINDEX SCHEMA CONCURRENTLY public;

Покажет ли это, какие ключи дублируются?

Интересно, что вернёт следующий запрос:

select id, username FROM users WHERE username_lower = 'robs' OR username = 'RobS':

Это не какая-то глупость вроде того, что в одной из записей имени пользователя есть пробелы или неразрывный символ / символ Unicode?

Думаю, да, хотя не уверен, есть ли лучший способ сделать это. На моём тестовом экземпляре команда выполняется без вывода каких-либо сообщений. При обновлении моего рабочего сайта у меня был только один дубликат, поэтому мой опыт может не подходить для вашей ситуации. В любом случае делайте резервную копию перед операциями с базой данных — я обычно создаю снимок в DigitalOcean, чтобы в случае проблем восстановление было быстрым и простым.

Думаю, я так же решал подобные проблемы в прошлом. Перестройте индекс; при сбое система укажет на дубликат, затем исправьте его и попробуйте снова. Повторяйте процесс.

Спасибо. Когда он не удаётся, как мне найти и удалить незавершённый новый индекс? Всё так просто, как добавить “_ccnew” к имени таблицы, на которой произошло сбой?

То есть, когда он падает на “users” из-за дубликата, я сначала исправляю дубликат, а затем:

DROP index ‘users_ccnew’;

После этого повторяю процесс заново? Всё так просто?

Спасибо,
Гуннар

Ах, да. Я оказался в ситуации, когда нашёл двух действительных (и активных) пользователей с одинаковыми username и username_lower:

  id   | username | (скрыто) | username_lower |
 42379 | DrC      | (скрыто) | drc            |
 47695 | DRC      | (скрыто) | drc            |

Похоже, мне нужно изменить как username, так и username_lower у второго пользователя. Как это сделать в psql?

Спасибо!
Гуннар