استعادة فاشلة - احتمال وجود مشكلة في Data Explorer

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

لا يمكن أن يكون السبب عدم تطابق الإصدار أو المخطط، لأن الخادم مبني من صورة جديدة للمنتدى العامل.

لقد حاولت إعادة البناء.

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

الشيء الغريب الوحيد الذي قمت به هو حذف ملفات PDF من مجلد التحميلات (…/uploads/original/1X/*.pdf) لتوفير بعض المساحة. سأحاول مرة أخرى دون تنفيذ هذه الخطوة، لكن يبدو من غير المرجح أن تكون هي السبب.

> [2019-11-30 01:17:44] بدأ 'admin' عملية الاستعادة!
> [2019-11-30 01:17:44] يتم الآن تحديد حالة الاستعادة على أنها جارية...
> [2019-11-30 01:17:44] التأكد من وجود /var/www/discourse/tmp/restores/default/2019-11-30-011744...
> [2019-11-30 01:17:44] جاري تنزيل الأرشيف إلى المجلد المؤقت...
> [2019-11-30 01:23:24] جاري فك ضغط الأرشيف، قد يستغرق ذلك بعض الوقت...
> [2019-11-30 01:27:52] لا يوجد ملف بيانات وصفية لاستخراجه.
> [2019-11-30 01:27:52] جاري التحقق من صحة البيانات الوصفية...
> [2019-11-30 01:27:52]   الإصدار الحالي: 20191129144706
> [2019-11-30 01:27:52]   الإصدار المراد استعادته: 20191120015344
> [2019-11-30 01:27:52] جاري استخراج ملف التصدير...
> [2019-11-30 01:50:57] أمر غير صالح \N
> [2019-11-30 01:50:57] أمر غير صالح \N
> 
> 
> < يتكرر حوالي 100 مرة >
> 
> [2019-11-30 01:51:07] أمر غير صالح \N
> [2019-11-30 01:54:13] أمر غير صالح \N
> [2019-11-30 01:54:13] استثناء: فشل psql
> [2019-11-30 01:54:14] /var/www/discourse/lib/backup_restore/restorer.rb:331:in `restore_dump'
> /var/www/discourse/lib/backup_restore/restorer.rb:75:in `run'
> /var/www/discourse/lib/backup_restore.rb:166:in `block in start!'
> /var/www/discourse/lib/backup_restore.rb:163:in `fork'
> /var/www/discourse/lib/backup_restore.rb:163:in `start!'
> /var/www/discourse/lib/backup_restore.rb:22:in `restore!'
> /var/www/discourse/app/controllers/admin/backups_controller.rb:119:in `restore'
> إلخ...

ومع ذلك، فإن خطأ invalid command \N نموذجي لعدم تطابق إصدار Postgres…

root@example:/var/www/discourse# psql --version
psql (PostgreSQL) 10.10 (Debian 10.10-1.pgdg100+1)

نفس الإصدار موجود على الخادم الجديد، والمنتدى يعمل بشكل صحيح.

أنا أميل إلى ربط تلك الأخطاء بتطابق إصدار PostgreSQL، لكنني رأيت أخطاء \N تلك في يوم آخر على نظام نفذت منه مساحة القرص (كنت أقوم باستعادة النسخة الاحتياطية إلى نفس النظام الذي أنشأ النسخة الاحتياطية). لم أكمل تشخيص المشكلة (كانت مشكلة غريبة أخرى كنت أعاني منها، وحلّت استعادة النسخة الاحتياطية إلى خادم آخر المشكلة؛ تساءلت عما إذا كانت الاستعادة إلى نفس الخادم كانت ستحل المشكلة).

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

إذًا، هل يستغرق الأمر 23 دقيقة قبل أن يفشل؟

الأمور تزداد سوءًا، لكن ربما نكون أقرب إلى المشكلة الحقيقية… بناءً على فرضية أنني أحتاج إلى مساحة تخزين أكبر، قمت بإنشاء مثيل جديد من صورتي، وهذه المرة بـ 100 جيجابايت بدلاً من 50 جيجابايت كما كان من قبل. (نسخ الاحتياطي بحجم 5 جيجابايت لكل منها ومخزنة على S3.) هذه المرة ظهرت لي رسالة خطأ صريحة: “No space left on device”. ومع ذلك، يُظهر أمر df وفرة من المساحة.

> [2019-11-29 22:42:58] التأكد من وجود /var/www/discourse/tmp/restores/default/2019-11-29-224258...
> [2019-11-29 22:42:58] تنزيل الأرشيف إلى مجلد tmp...
> [2019-11-29 22:45:46] فك ضغط الأرشيف، قد يستغرق هذا بعض الوقت...
> [2019-11-29 22:51:46] لا يوجد ملف بيانات وصفية لاستخراجه.
> [2019-11-29 22:51:46] التحقق من صحة البيانات الوصفية...
> [2019-11-29 22:51:46]   الإصدار الحالي: 20191129144706
> [2019-11-29 22:51:46]   الإصدار المستعاد: 20191108000414
> [2019-11-29 22:51:46] استخراج ملف التصدير...
> [2019-11-29 22:53:47] استثناء: لا توجد مساحة متبقية على الجهاز @ io_write - /shared/tmp/restores/default/2019-11-29-224258/dump.sql

> [ec2-user@ip-172-31-47-237 discourse]$ df / -h
> Filesystem      Size  Used Avail Use% Mounted on
> /dev/xvda1       99G   28G   71G  28% /

من المثير للاهتمام، أن المجلد المشار إليه فارغ:

> /var/www/discourse# ls /shared/tmp/*
> /shared/tmp/backups:
> < empty >
> /shared/tmp/restores:
> < empty >

هل يمكن أن تكون المشكلة مرتبطة بملف التبديل (swap file)؟ مثيل EC2 هو من نوع t2.small بذاكرة 2 جيجابايت، لذا قمت منذ فترة طويلة بإنشاء ملف تبديل على المنتدى العامل. أفترض أن ملف التبديل سيتم نسخه تلقائيًا إلى المثيل الجديد. لست خبيرًا، لكنني أعتقد أن ملف التبديل موجود، لأنه لم يسمح لي بإنشاء ملف جديد، ولأن:

> /var/www/discourse# swapon -s
> Filename                                Type            Size    Used    Priority
> /swapfile                               file            2097148 1024    -2

قد تكون المشكلة مرتبطة بـ inode. ماذا يخبرك الأمر df -i؟

df -i
نظام الملفات العقد IUsed IFree IUse% مرفق على
devtmpfs 252562 437 252125 1% /dev
tmpfs 255203 1 255202 1% /dev/shm
/dev/xvda1 6553600 737194 5816406 12% /

أخرج من دوري، لكنني أفترض أن هذا ليس سيئًا؟

لا، لا بأس.

لا يحدث خطأ ‘لا يوجد مساحة متبقية على الجهاز’ فقط عندما تنفد الجيجابايتات من الجهاز، بل يحدث أيضًا عندما ينفد نظام الملفات من الـ inodes. لكن من الواضح أن هذه ليست المشكلة هنا. (فستكون iUse% عند 100% في هذه الحالة).

ما زال الأمر بلا جدوى. ظننت أنني سأحاول الاستعادة على مثيل Lightsail جديد، بدلاً من تشغيل صورة AMI لمثيل EC2 الذي يعمل بشكل صحيح. لا يزال الفشل يحدث، لكن الرسائل تختلف إلى حد ما.

كلا المثيلين القديم والجديد محدثان، وكلاهما تثبيت Docker قياسي، وكلاهما يشغل نفس إصدار PostgreSQL:

psql --version
psql (PostgreSQL) 10.10 (Debian 10.10-1.pgdg100+1)

هل هذا طبيعي:

Creating missing functions in the discourse_functions schema
Cannot restore into different schema, restoring in-place

هل يمكن أن يكون ذلك مرتبطًا بالملحقات؟ لدي عدة ملحقات، مدعومة ومخصصة، مثبتة على موقع “المصدر”. بعضها يستخدم حقول مستخدم مخصصة. لقد حاولت الاستعادة إلى مواقع “وجهة” نظيفة مع وبدون ملحقات.

هل لديك أي تلميحات حول كيفية البدء في مقارنة المخططات؟

> [2019-12-07 04:51:36] 'admin' has started the restore!
> [2019-12-07 04:51:36] Marking restore as running...
> [2019-12-07 04:51:36] Making sure /var/www/discourse/tmp/restores/default/2019-12-07-045136 exists...
> [2019-12-07 04:51:36] Downloading archive to tmp directory...
> [2019-12-07 04:53:49] Unzipping archive, this may take a while...
> [2019-12-07 04:57:12] No metadata file to extract.
> [2019-12-07 04:57:12] Validating metadata...
> [2019-12-07 04:57:12]   Current version: 20191129144706
> [2019-12-07 04:57:12]   Restored version: 20191120015344
> [2019-12-07 04:57:12] Extracting dump file...
> [2019-12-07 04:59:10] Creating missing functions in the discourse_functions schema
> [2019-12-07 04:59:11] Cannot restore into different schema, restoring in-place
> [2019-12-07 05:05:02] ERROR:  current transaction is aborted, commands ignored until end of transaction block
> [2019-12-07 05:05:03] ERROR:  current transaction is aborted, commands ignored until end of transaction block
> < repeats about 100 times >
> [2019-12-07 05:05:03] ERROR:  current transaction is aborted, commands ignored until end of transaction block
> [2019-12-07 05:05:03] EXCEPTION: psql failed
> [2019-12-07 05:05:03] /var/www/discourse/lib/backup_restore/restorer.rb:331:in `restore_dump'
> /var/www/discourse/lib/backup_restore/restorer.rb:75:in `run'
> /var/www/discourse/lib/backup_restore.rb:166:in `block in start!'
> /var/www/discourse/lib/backup_restore.rb:163:in `fork'
> /var/www/discourse/lib/backup_restore.rb:163:in `start!'
> /var/www/discourse/lib/backup_restore.rb:22:in `restore!'
> /var/www/discourse/app/controllers/admin/backups_controller.rb:119:in `restore'
> < rest of traceback >

نعم، هذا طبيعي.

من الواضح أن هناك مشكلة ما داخل PostgreSQL. هل راجعت سجلات (logs) النظام؟

احتمال بعيد: هل يمكن أن يكون هذا متعلقًا بالذاكرة؟ هل يمكنك محاولة مراقبة مخرجات free -m أثناء الاستعادة ورؤية ما إذا كان النظام ينفد من الذاكرة (الافتراضية)؟

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

نعم، يجب عليك ذلك - لن تقوم عملية الاستعادة بذلك نيابةً عنك.

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

لقد قمت بإنشاء جهاز Lightsail جديد بسعة 20 دولارًا وذاكرة 4 جيجابايت. راقبت الأمر ‘free -m’ أثناء عملية الاستعادة. كانت الذاكرة الحرة والمتاحة دائمًا وفيرة.

تختلف الأخطاء المحددة اعتمادًا على ما إذا كانت الإضافات مثبتة أم لا، لكنها ترجع على الأرجح إلى نفس السبب الجذري. في هذه الحالة، لم قمت بتثبيت الإضافات قبل النسخ الاحتياطي. هناك فئتان من الأخطاء:

  1. في سجلات postgres، أرى عددًا كبيرًا من هذه الرسائل، أحيانًا عند الحرف 34 وأحيانًا عند الحرف 41.

discourse@discourse ERROR: العلاقة “user_auth_tokens” غير موجودة عند الحرف 34

هذه الأخطاء ليس لها مخرج مقابله في صفحة المسؤول > النسخ الاحتياطي > السجل، وتستمر عملية الاستعادة لعدة دقائق رغم هذه الأخطاء.

  1. الخطأ الثاني يعتمد على ما إذا كانت الإضافات مثبتة. في هذه الحالة، لم أقم بتثبيتها، لذا حصلت على خطأ يتعلق بـ Data Explorer، وهناك هو حيث تبدأ الأمور في التعقد.

في المسؤول > النسخ الاحتياطي > السجلات:

[2019-12-07 07:38:34] CREATE INDEX
[2019-12-07 07:38:34] CREATE INDEX
[2019-12-07 07:38:34] ERROR: تعذر إنشاء فهرس فريد “index_plugin_store_rows_on_plugin_name_and_key”
[2019-12-07 07:38:34] DETAIL: المفتاح (plugin_name, key)=(discourse-data-explorer, q:-6) مكرر.
[2019-12-07 07:38:34] ERROR: تم إلغاء المعاملة الحالية، والأوامر يتم تجاهلها حتى نهاية كتلة المعاملة
[2019-12-07 07:38:34] ERROR: تم إلغاء المعاملة الحالية، والأوامر يتم تجاهلها حتى نهاية كتلة المعاملة
< يتكرر 1000 مرة ثم يتوقف >

والمخرج المقابل في سجل postgres:

2019-12-07 07:38:34.718 UTC [8991] discourse@discourse LOG: المدة: 165.427 مللي ثانية العبارة: CREATE INDEX index_notifications_on_user_
id_and_topic_id_and_post_number ON public.notifications USING btree (user_id, topic_id, post_number);
2019-12-07 07:38:34.767 UTC [8991] discourse@discourse ERROR: تعذر إنشاء فهرس فريد “index_plugin_store_rows_on_plugin_name_an
d_key”
2019-12-07 07:38:34.767 UTC [8991] discourse@discourse DETAIL: المفتاح (plugin_name, key)=(discourse-data-explorer, q:-6) مكرر.
2019-12-07 07:38:34.767 UTC [8991] discourse@discourse STATEMENT: CREATE UNIQUE INDEX index_plugin_store_rows_on_plugin_name_and_key O
N public.plugin_store_rows USING btree (plugin_name, key);
2019-12-07 07:38:34.984 UTC [8991] discourse@discourse ERROR: تم إلغاء المعاملة الحالية، والأوامر يتم تجاهلها حتى نهاية كتلة المعاملة
2019-12-07 07:38:34.984 UTC [8991] discourse@discourse STATEMENT: CREATE INDEX index_policy_users_on_post_policy_id_and_user_id ON pub
lic.policy_users USING btree (post_policy_id, user_id);

أعتقد أن المشكلة رقم 1 ليست قاتلة وهي مجرد أثر جانبي لاستعادة البيانات في المكان نفسه.

قد تفكر في (تصدير وحذف) جميع استعلامات مستكشف البيانات وإزالة مكون مستكشف البيانات الإضافي قبل إنشاء النسخة الاحتياطية.

بدلاً من ذلك، هل يمكنك نشر المحتوى ذي الصلة من جدول plugin_store_rows؟

في الواقع، توجد استعلامات مكررة مع أزواج مكررة من (plugin_name, key)، مثل q:-11 و q:-2، ولكن مع معرفات فريدة. لا أرى أي نمط بين المكررات، كأنها ليست استعلاماتي المفضلة أو شيء من هذا القبيل.

لذلك، ستكون خطوتي التالية هي إزالة المكررات، عمل نسخة احتياطية، ثم محاولة الاستعادة منها.

SELECT id, plugin_name, key from plugin_store_rows
WHERE plugin_name = ‘discourse-data-explorer’
ORDER BY key

id plugin_name key
1138 discourse-data-explorer q:-1
1136 discourse-data-explorer q:-10
813 discourse-data-explorer q:10
1142 discourse-data-explorer q:-11
1397 discourse-data-explorer q:-11
825 discourse-data-explorer q:11
889 discourse-data-explorer q:13
1004 discourse-data-explorer q:14
1005 discourse-data-explorer q:15
1043 discourse-data-explorer q:17
1044 discourse-data-explorer q:18
514 discourse-data-explorer q:-2
1249 discourse-data-explorer q:-2
764 discourse-data-explorer q:2
1053 discourse-data-explorer q:21
1066 discourse-data-explorer q:22
1082 discourse-data-explorer q:23
1097 discourse-data-explorer q:24
1131 discourse-data-explorer q:26
1132 discourse-data-explorer q:27
1134 discourse-data-explorer q:28
1135 discourse-data-explorer q:29
775 discourse-data-explorer q:3
1137 discourse-data-explorer q:30
1140 discourse-data-explorer q:31
1141 discourse-data-explorer q:32
1143 discourse-data-explorer q:33
1149 discourse-data-explorer q:34
1155 discourse-data-explorer q:35
1156 discourse-data-explorer q:36
1157 discourse-data-explorer q:37
1158 discourse-data-explorer q:38
1161 discourse-data-explorer q:39
513 discourse-data-explorer q:-4
777 discourse-data-explorer q:4
1211 discourse-data-explorer q:40
1215 discourse-data-explorer q:41
1223 discourse-data-explorer q:42
1224 discourse-data-explorer q:43
1225 discourse-data-explorer q:44
1226 discourse-data-explorer q:45
1269 discourse-data-explorer q:46
1272 discourse-data-explorer q:47
1273 discourse-data-explorer q:48
1274 discourse-data-explorer q:49
1279 discourse-data-explorer q:50
1281 discourse-data-explorer q:51
1282 discourse-data-explorer q:52
1301 discourse-data-explorer q:53
1349 discourse-data-explorer q:54
1369 discourse-data-explorer q:55
1373 discourse-data-explorer q:56
1384 discourse-data-explorer q:57
1387 discourse-data-explorer q:58
1396 discourse-data-explorer q:59
1222 discourse-data-explorer q:-6
1348 discourse-data-explorer q:-6
781 discourse-data-explorer q:6
763 discourse-data-explorer q:-7
782 discourse-data-explorer q:7
515 discourse-data-explorer q:-8
791 discourse-data-explorer q:8
1139 discourse-data-explorer q:-9
798 discourse-data-explorer q:9
507 discourse-data-explorer q:_id

في الواقع، كيف يمكنني حذف التكرارات؟ جميع العناصر الثلاثة مملوكة لـ “system”، لذا لا يمكنني تحريرها أو حذفها.

وجدت النمط. عند تشغيل استعلام مملوك للنظام، يتم إنشاء نسخة مكررة، مما يكسر عملية الاستعادة بوضوح.

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

  1. كيف يمكنني تتبع ما الخطأ في موقع الإنتاج الخاص بي؟

  2. كيف يمكنني إزالة الاستعلامات المكررة، حيث إنها مملوكة للنظام؟ هل عليّ استخدام أمر sudo -u postgres psql discourse...؟ يبدو الأمر مخيفًا.

لجعل النسخة الاحتياطية قابلة للاستعادة، يمكنك على الأرجح حذف الأسطر المكررة من ملف SQL المحفوظ.

هل من الممكن أن تكون قاعدة بيانات المطور تفتقر إلى هذا الفهرس لسبب ما؟