Ошибка импорта резервной копии: «не удалось создать уникальный индекс»

Здравствуйте,

Я пытаюсь перенести форум на новый сервер. На обоих серверах запущена последняя версия Discourse в Docker. При импорте резервной копии через командную строку возникает следующая ошибка:

ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 418) is duplicated.
EXCEPTION: psql failed: DETAIL:  Key (path, incoming_domain_id)=(/search/, 418) is duplicated.

Похоже, это та же или схожая ошибка, что и здесь:

Однако в моём случае дублирующиеся записи находятся по пути /search/, а не /m/search, как в упомянутой теме.

Я подключился к контейнеру на старом сервере (./launcher enter app) и в консоли Rails (rails c) попытался найти дублирующиеся записи с помощью:

IncomingReferer.where(path: "/search")
и
IncomingReferer.where("path LIKE '%/search%'")

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

Вы пробовали использовать графический интерфейс администратора?

Нет, я предположил, что импорт через GUI запустит тот же процесс импорта? Сейчас попробую.

Я подозреваю, что это означает наличие повреждённого индекса. Какую версию Postgres вы используете?

Что-то вроде:

cd /var/discourse
cat shared/standalone/postgres_data/PG*

(Я точно не помню имя файла Postgres).

Вы можете поискать здесь «postgres corrupt index» и найти тему, которую я когда-то написал о том, как находить и удалять такие проблемные записи.

В основном вы пытаетесь перестроить этот индекс, удаляете записи, на которые он жалуется, а затем снова пытаетесь перестроить индекс, пока он не будет успешно перестроен.

Только что попробовал импорт через GUI, результат точно такой же:

На старом сервере нет файла PG_VERSION. Как узнать, какая версия запущена? Сегодня я обновил установку в докере до последней версии.

Новый сервер (только что настроенный) работает на PostgreSQL V13.

cat shared/standalone/postgres_data/PG_VERSION
13

Есть ли рекомендованная процедура для выполнения этого?

У меня была тема с некоторыми подсказками, но я больше её не вижу. С момента обновления PostgreSQL до версии 12 прошло почти год.

  reindex index index_incoming_referers_on_path_and_incoming_domain_id;

И

 ActiveRecord::Base.connection.execute('reindex index index_incoming_referers_on_path_and_incoming_domain_id;')

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

Хорошо, я исправил это. Я выполнил следующие действия:

Войти в контейнер

./launcher enter app

Подключиться к базе данных

su postgres -c 'psql discourse'

Попробовать найти дубликаты

discourse=# select * from incoming_referers where path LIKE '%/search/' ORDER BY incoming_domain_id;

  id  |    path    | incoming_domain_id
------+------------+--------------------
 3339 | /search/   |                 33
 6257 | /search/   |                 91
 1567 | /search/   |                298
 1777 | /search/   |                341
 3010 | /search/   |                418
 6247 | /search/   |                418
 4293 | /search/   |                644
 2899 | /search/   |                653
 3447 | /search/   |                793
 3696 | /search/   |                852
 4395 | /a/search/ |               1050
 6968 | /search/   |               1305
 5634 | /search/   |               1387
 5834 | /search/   |               1437
 6519 | /search/   |               1637
 7127 | /search/   |               1787
 7280 | /search/   |               1827
(17 rows)

Удалить дубликат

DELETE FROM incoming_referers WHERE path LIKE '%/search/' AND id IN (6247);

Затем выполнить перестроение

discourse=# REINDEX SCHEMA CONCURRENTLY public;
WARNING:  cannot reindex invalid index "public.incoming_referers_pkey_ccnew" concurrently, skipping
WARNING:  cannot reindex invalid index "public.index_incoming_referers_on_path_and_incoming_domain_id_ccnew" concurrently, skipping
WARNING:  cannot reindex invalid index "pg_toast.pg_toast_20732_index_ccnew" concurrently, skipping
REINDEX

Затем я создал новую резервную копию, скопировал её на новый сервер, и импорт прошёл успешно :smiley:

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

Большое спасибо за вашу помощь.