فشل الاستعادة: تعذر إنشاء فهرس فريد

مرحبًا، فشل استعادة النسخة الاحتياطية مع الخطأ التالي:

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

وجدت موضوعًا مشابهًا لكن لم أستطع فهم ما يجب فعله: https://meta.discourse.org/t/getting-this-error-during-restore-could-not-create-unique-index/

أقدر أي مساعدة خطوة بخطوة للمبتدئين لحل المشكلة.

ظهرت هذه المشكلة المتعلقة بجدول incoming_referers عدة مرات مؤخرًا. لست متأكدًا من سبب تسبب هذا الجدول تحديدًا في المشاكل، لكن يبدو من المرجح أن تكون المشاكل مرتبطة ببعضها. ربما يكون لدى شخص آخر في فريق Discourse أفكار حول ما قد يتسبب في إنشاء سجلات مكررة.

هل لا تزال لديك إمكانية الوصول إلى الموقع الذي أنشأت فيه ملف النسخ الاحتياطي؟ إذا كان الأمر كذلك، فإن الحل هو حذف السجل المكرر من قاعدة البيانات ثم إنشاء ملف نسخ احتياطي جديد. للقيام بذلك، ستحتاج إلى الاتصال عبر SSH بالخادم القديم والانتقال (cd) إلى دليل /var/discourse:

cd /var/discourse

ثم قم بتشغيل

./launcher enter app

بعد ذلك، أدخل وحدة تحكم Rails باستخدام الأمر التالي:

rails c

يجب أن ترى بعد ذلك موجهًا يشبه هذا:

[1] pry(main)>

حاول تشغيل الأمر التالي من وحدة تحكم Rails وأخبرنا بما يعيده:

IncomingReferer.where(path: "/m/search")

يجب أن يعيد مصفوفة تحتوي على سجلين أو أكثر.

شكرًا لك.
سأقوم بتشغيل هذا في الصباح وسأبلغك بالنتائج.

هذا من التثبيت القديم - يبدو أنه سجل واحد فقط؟

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x00005638d834b130
  id: 5153,
  path: "/m/search",
  incoming_domain_id: 25>]
[2] pry(main)>

تعديل: جربته على الخادم الجديد أيضًا. يظهر:

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> []
[2] pry(main)>

شكرًا لك على التحقق من ذلك! النتيجة التي حصلت عليها هي نفسها التي رأيتها مع موقع آخر في وقت سابق من اليوم. إنها مشكلة قابلة للحل، لكنني سأحاول أن أحصل على أحد مهندسينا لمراجعة ما يحدث.

كان الغرض الأساسي من نقل الخوادم هو أنني كنت أستخدم Debian 8 الذي سيخرج من الدعم.
مع هذه المشكلة المتعلقة بالاستعادة، اتبعت طريق الترقية إلى Debian 9 على نفس الخادم. وقد نجح ذلك، مما منحنا بعض الراحة مؤقتًا.
شكرًا لكم على دعمكم.

استبدل هذا السطر

يجب عليك إجراء بحث ضبابي (fuzzy search) حتى لا يفترض أن الفهرس يعمل بشكل صحيح. أعتقد أن علامة النسبة المئوية الواحدة كافية إذا كانت في البداية.

يمكنك ببساطة حذف السجل الإضافي. ولكن للقيام بذلك بشكل صحيح، يجب عليك تحديث الجدول الآخر الذي يرتبط بهذا الجدول. يجب علي البحث عن ذلك في كل مرة لأن هناك عدة جداول مختلفة ترتبط ببعضها.

يُلقى باللوم على هذه المشكلة في ملحقات الطرف الثالث، وهو ما لا يبدو منطقياً. يبدو أن الخطأ من postgres، لكنني لا أعرف. أواجه هذه المشكلة عدة مرات في الشهر، يبدو ذلك (النقاط) عبر عدد من المواقع.

أواجه أيضًا مشكلة في مفتاح مكرر، هل توجد طريقة إصلاح موثقة؟

discourse=# REINDEX SCHEMA CONCURRENTLY public;
    ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

[1] pry(main)> IncomingReferer.where(path: "/m/search")
=> [#<IncomingReferer:0x0000557176d3f210 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557176d925c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]

رغم أنني قمت للتو بترقية خادمي مباشرةً، وبالتالي لن أعيد التثبيت على خادم جديد بعد الآن، فقد جربت ذلك بدافع الفضول ولم أجد أي سجلات باستخدام البحث الضبابي:

[1] pry(main)> IncomingReferer.where(path: "%/m/search%")
=> []
[2] pry(main)> IncomingReferer.where(path: "%/m/search")
=> []
[3] pry(main)> IncomingReferer.where(path: "/m/search%")
=> []

يجب عليك استخدام LIKE لكي تعمل الرموز البدئية:

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

أثار ذلك عددًا كبيرًا من المفاتيح المكررة.

[1] pry(main)> IncomingReferer.where("path LIKE '%/m/search%'")
=> [#<IncomingReferer:0x0000557eaa7ed488 id: 408, path: "/m/search", incoming_domain_id: 26>,
 #<IncomingReferer:0x0000557eaabd80c0 id: 1508, path: "/m/search", incoming_domain_id: 45>,
 #<IncomingReferer:0x0000557eaabe3268 id: 2216, path: "/m/search", incoming_domain_id: 420>,
 #<IncomingReferer:0x0000557eaabe2f20 id: 3081, path: "/m/search", incoming_domain_id: 230>,
 #<IncomingReferer:0x0000557eaabe2c00 id: 33210, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe2908 id: 44231, path: "/m/search", incoming_domain_id: 4>,
 #<IncomingReferer:0x0000557eaabe27c8 id: 42228, path: "/m/search", incoming_domain_id: 26>]

سأقوم ببساطة بحذف جميع الصفوف المكررة… فهذه المعلومات لا تملك قيمة كبيرة.

يسعدني القيام بذلك، هل يمكنك تزويدي بالأمر الصحيح؟ لست على دراية بـ postgres على وجه التحديد، لكنني أعرف SQL.

هذا جيد أن نسمع. لقد كنت أقوم بتحديث الجدول الآخر الذي يرتبط بهذه الصفوف بشكل شاق. إنها عملية مرهقة للغاية لأنني لا أستطيع تذكر ما هو أبدًا، لذا فإنها المرة الأولى التي أكرر فيها ذلك مرارًا وتكرارًا.

IncomingReferer.find(44231).destroy
IncomingReferer.find(42228).destroy

تمت إزالة نسختي التكرار بنجاح، لكن إعادة بناء الفهارس لاحقًا أفرزت أخطاءً جديدة. هل هذه مشكلة كبيرة؟ كيف نصلحها أو نحذف صف البحث 3433؟

[1] pry(main)> IncomingReferer.find(44231).destroy
=> #<IncomingReferer:0x000055734c65d8e8 id: 44231, path: "/m/search", incoming_domain_id: 4>
[2] pry(main)> IncomingReferer.find(42228).destroy
=> #<IncomingReferer:0x000055734cd81a70 id: 42228, path: "/m/search", incoming_domain_id: 26>
postgres=# \connect discourse
أنت الآن متصل بقاعدة البيانات "discourse" كمستخدم "postgres".
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_2782645_index_ccnew" concurrently, skipping
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 3433) is duplicated.
CONTEXT:  parallel worker

إليك الكود المسؤول عن الإنشاء… يجب أن يتعامل معه بشكل صحيح، لكن يمكننا تحديثه إلى إدراج ON CONFLICT إذا لزم الأمر؟

حاولت إعادة بناء هذه الفهارس الأربعة يدويًا. نجح اثنان، وفشل اثنان. هل يجب أن أحذف هاتين الصفحتين المكررتين؟

discourse=# REINDEX INDEX CONCURRENTLY "public"."incoming_referers_pkey_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "public"."index_incoming_referers_on_path_and_incoming_domain_id_ccnew";
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_cc_ccnew"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1861) is duplicated.
discourse=# REINDEX INDEX CONCURRENTLY "pg_toast"."pg_toast_2782645_index_ccnew";
REINDEX
discourse=# REINDEX INDEX CONCURRENTLY "index_incoming_referers_on_path_and_incoming_domain_id_ccnew1";
ERROR:  could not create unique index "index_incoming_referers_on_path_and_incoming_domain_id_c_ccnew1"
DETAIL:  Key (path, incoming_domain_id)=(/search/, 1905) is duplicated.

نعم، يرجى حذف الصفوف المكررة

@riking تلف الفهارس في PostgreSQL هو خلل في PostgreSQL نفسه وليس في Discourse. بالتأكيد يمكننا تحسين أداء عملية الإدراج تلك، لكن الخلل في PostgreSQL يتطلب إصلاحًا من قبل فريق PostgreSQL.

أعتقد أن الأمر يتعلق بإغلاق غير لائق لمحرك قاعدة البيانات، ربما بسبب انقطاع التيار الكهربائي.

هذا تفسير معقول. هل يؤدي الأمر ./launcher shutdown app (أو إعادة البناء) إلى إغلاق نظيف لـ postgres بطريقة ما؟ لكنني أراهن أن التحديث غير المراقب لا يعرف كيفية إغلاق حاويات docker بشكل نظيف، أليس كذلك؟