Восстановление резервной копии не удалось

У меня возникла следующая проблема. Я веду форум Discourse уже более 10 лет, и поскольку некоторое время не мог устанавливать обновления, я решил настроить новый сервер:

Старый сервер работает на версии: 3.4.0.beta4-dev

Новый сервер: Последняя версия

Размер резервной копии уже составляет 673,2 МБ в формате .gz, без учёта вложений.

К сожалению, процесс восстановления постоянно прерывается. В файле журнала содержится следующая ошибка:

[2026-06-16 07:54:52] ERROR:  could not create unique index «index_incoming_referers_on_path_and_incoming_domain_id»
[2026-06-16 07:54:52] DETAIL:  Key (path, incoming_domain_id)=(//, 5) is duplicated.
[2026-06-16 07:54:52] EXCEPTION: psql failed: DETAIL:  Key (path, incoming_domain_id)=(//, 5) is duplicated.

[2026-06-16 07:54:52] /var/www/discourse/lib/backup_restore/database_restorer.rb:93:in 'BackupRestore::DatabaseRestorer#restore_dump'
/var/www/discourse/lib/backup_restore/database_restorer.rb:26:in 'BackupRestore::DatabaseRestorer#restore'
/var/www/discourse/lib/backup_restore/restorer.rb:61:in 'BackupRestore::Restorer#run'
/var/www/discourse/script/spawn_backup_restore.rb:20:in 'Object#restore'
/var/www/discourse/script/spawn_backup_restore.rb:33:in 'block in '
/var/www/discourse/script/spawn_backup_restore.rb:4:in 'Kernel#fork'
/var/www/discourse/script/spawn_backup_restore.rb:4:in ''
[2026-06-16 07:54:52] Trying to rollback...

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

Заранее благодарю за любую помощь!

Привет и приветствуем с возвращением после девяти лет!

Очень круто видеть такой долгоживущий форум.

Думаю, это та же проблема с индексом, о которой говорилось здесь: Can't restore due to corrupt indexes (with some clues on how to deal with corrupt indexes)

Суть в следующем:

Таблица incoming_referers отслеживает пути URL, по которым посетители попали на ваш форум. У неё есть уникальный индекс, что означает отсутствие двух строк с одинаковой комбинацией пути и домена.

В вашей базе данных есть две строки с path='//' и incoming_domain_id=5. Когда система пытается восстановить этот уникальный индекс во время восстановления резервной копии, она обнаруживает дубликат и прерывает всю транзакцию восстановления.

Так что вам нужно найти и удалить этот дубликат в таблице incoming_referers, а затем создать новую резервную копию для восстановления на новом сервере.

Я нашел эту тему с инструкциями, которая может вам помочь

Большое спасибо за быстрый ответ, я попробую!

(сообщение удалено автором)

Извините, что беспокою, но, кажется, я делаю что-то не так — после выполнения команды «discourse=# select * from incoming_referers where path LIKE ‘%/search/’ ORDER BY incoming_domain_id;» возникает ошибка синтаксиса.

Вы набрали «discourse=#»? Если да, попробуйте снова без этого.

Затем я получаю сообщение об ошибке «bash: syntax error near unexpected token `from’»

Для справки, я нахожусь в: root@community-app:/var/www/discourse#

Ах, вы ещё не в базе данных.

Сначала выполните psql -U discourse discourse (я думаю)

Затем приглашение командной строки должно измениться на discourse=#

Хорошо, я также установил «apt install postgresql-client-common». Однако ошибка «Error: You must install at least one postgresql-client- package.» всё ещё сохраняется.

Хм, этого я не знаю.

Может быть, попробуйте напрямую через консоль Rails: rails c

с

ActiveRecord::Base.connection.execute(<<~SQL)
  SELECT id, path, incoming_domain_id 
  FROM incoming_referers 
  WHERE path = '//' 
  AND incoming_domain_id = 5
SQL

Это может дать вам тот же результат, не обращаясь к базе данных.

Получилось?

Мне очень, очень стыдно за своё глупое поведение.

Я установил Rails, и, кажется, всё получилось. Я также запустил его через «rails c». Это тоже сработало, но при вводе команды получилось вот так:

“root@community:/var/discourse# ActiveRecord::Base.connection.execute(<<~SQL)
-bash: syntax error near unexpected token `<<’
root@community:/var/discourse# SELECT id, path, incoming_domain_id
SELECT: команда не найдена
root@community:/var/discourse# FROM incoming_referers
FROM: команда не найдена
root@community:/var/discourse# WHERE path = ‘//’
WHERE: команда не найдена
root@community:/var/discourse# AND incoming_domain_id = 5
AND: команда не найдена
root@community:/var/discourse# SQL”

Не переживайте, возможно, мои инструкции просто не очень хорошие :slight_smile:

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

В любом случае, когда вы окажетесь в Rails, приглашение командной строки должно измениться. Судя по вашему выводу, вы пока не вошли в консоль Rails.

Даже не уверен, что вы находитесь в самом контейнере. Вы уже выполнили ./launcher enter app?

OK, да, в этом и была проблема. Запрос теперь выполнился успешно, и я получил такой ответ:

#<PG::Result:0x00007fbde9732ef0 status=PGRES_TUPLES_OK ntuples=1 nfields=3 cmd_tuples=1>

Итак, ntuples=1 означает, что была найдена только одна строка, а согласно журналу ошибок, должна быть дублирующаяся… :woman_shrugging:

Извините, я не знаю, что ещё попробовать. Рекомендую поискать на форуме — кажется, есть много похожих случаев (см. ниже в разделе «Связанные темы»). Возможно, вы найдёте там подсказку.

(Или подождите, пока пройдёт кто-то более компетентный ;))

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

На СТАРОМ сервере откройте консоль Rails:

./launcher enter app
rails c

Затем по очереди вставьте следующие строки:

db = ActiveRecord::Base.connection.current_database
DB.exec("DELETE FROM incoming_referers a USING incoming_referers b WHERE a.id > b.id AND a.path = b.path AND a.incoming_domain_id = b.incoming_domain_id")
DB.exec("REINDEX DATABASE #{ActiveRecord::Base.connection.quote_table_name(db)}")

Это удалит дубликаты и перестроит все индексы (на случай, если затронуты и другие таблицы).

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

Большое спасибо. К сожалению, на последней строке появляется сообщение об ошибке:

«PG::InsufficientPrivilege: ERROR: must be owner of database discourse»

На самом деле это уже должен быть «стандартный сервер». Установка выполнялась командой «Discourse» за разовую плату.

А, верно. Попробуем так.

Выйдите из консоли Rails (введите exit), и, оставаясь внутри контейнера (./launcher enter app), выполните:

su postgres -c "reindexdb discourse"

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

Огромное спасибо! Всё получилось просто замечательно, и без вашей помощи у меня не было бы ни единого шанса, и мне, скорее всего, пришлось бы с болью в душе начинать форум с нуля! ОГРОМНОЕ СПАСИБО!

Также спасибо @chapoi за отличную поддержку!