استخدام الذاكرة المرتفع لـ Import Script

أحاول إجراء اختبار استيراد من لوحة المناقشات الحالية لدينا. لدينا حوالي 25 مليون منشور للاستيراد (منشورات عادية + محادثات خاصة)، ولتسريع هذه العملية، قمت بإنشاء نسخ متعددة من سكريبت الاستيراد لتشغيلها في وقت واحد وتقسيم حمل المواضيع. عمل هذا بشكل جيد لبضعة أيام، ومع مرور الوقت لاحظت أن استخدام الذاكرة لكل عملية يزداد ببطء حتى يصل إلى حوالي 2 جيجابايت لكل منها. ثم نفدت ذاكرة الخادم في النهاية وقُتلت قاعدة بيانات MySQL المصدرية عند علامة 16 مليون منشور.

لقد قمت بزيادة ذاكرة النظام من 24 جيجابايت إلى 32 جيجابايت، ولكن الآن عند محاولة إعادة تشغيل حتى عملية استيراد واحدة واستئنافها من حيث توقفت، تستهلك هذه العملية حوالي 10 جيجابايت من الذاكرة فورًا قبل حتى البدء في استيراد المنشورات. بينما كنت قادرًا سابقًا على تشغيل 8 عمليات استيراد متزامنة، يمكنني الآن تشغيل عمليتين فقط في مجموعة ذاكرة أكبر. لماذا يوجد هذا التباين الهائل في استخدام الذاكرة بين التثبيت النظيف وإعادة تشغيل الاستيراد بعد الفشل؟ هل هناك أي طريقة لتقليل هذا البصمة الذاكرة حتى أتمكن من تسريع عملية الاستيراد مرة أخرى؟ سيكون خادم ذاكرته 128 جيجابايت - 256 جيجابايت باهظ الثمن (ولن يكون ضروريًا بعد الاستيراد)، وتشغيل عمليتي استيراد فقط سيعني أن الاستيراد سيستغرق أسابيع لإكماله.

يبدو وكأنه تعبير نمطي عالق في حلقة تكرار أو شيء مشابه. اطبع رسائل التصحيح وتخطَّ الصف الذي يسبب المشكلة أثناء الاستيراد.

يبدو أن استخدام الذاكرة يحدث بالكامل خلال مرحلة “تحميل المنشورات الحالية…” (أو المواضيع) عند بدء تشغيل سكريبت الاستيراد، وليس أثناء معالجة المنشورات الفعلية. بناءً على ما أستطيع ملاحظته، تقوم هذه المرحلة بسحب معلومات المنشورات والمواضيع من قاعدة البيانات، ولا ينبغي أن يكون هناك أي تدخل من التعابير النمطية (regex).

يبدو أن المتغيرات @posts و @topics تُستخدم لأغراض مثل طريقة “topic_lookup_from_imported_post_id”. هذا منطقي، باستثناء أنه عندما كان السكريبت يعمل في التشغيل الأولي، لم يقترب استخدام الذاكرة أبدًا من المستوى الذي أراه الآن، ومع ذلك لا تزال هذه الطرق تعمل بشكل صحيح.

هل راجعت سكريبتات الاستيراد الجماعي؟ قد يكون لها استهلاك أقل للذاكرة.

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

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

أنا لست على علم بأي سكريبتات استيراد جماعي. هل توجد هذه السكريبتات في مجلد import_scripts في مكان ما؟

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

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

انظر إلى discourse/script/bulk_import at main · discourse/discourse · GitHub.

بالنسبة للمواضيع والمنشورات، فإن تشغيل عمليات استيراد متوازية ليس عمليًا في الواقع، حيث لا يمكن استيراد منشور في موضوع ما إلا إذا تم استيراد الموضوع نفسه وجميع المنشورات السابقة له مسبقًا. يُفترض أنه يمكن تشغيل عمليات متوازية للمستخدمين والمواضيع، لكن ليس للمنشورات إلا إذا قمت بإعادة كتابة السكربت لجلب جميع منشورات موضوع معين، مما يسمح بالاستيراد المتوازي؛ وهذا ممكن بالتأكيد، لكنه ليس الطريقة التي تعمل بها أي من السكربتات التي استخدمتها. ومع ذلك، ستظل تواجه مشكلة الاحتفاظ بخريطة معرفات من القديمة إلى الجديدة في ذاكرة الوصول العشوائي (RAM).

استيراد 25 مليون منشور ليس أمرًا مريحًا. :slight_smile:

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

هذا هو الجزء الأكثر إرباكًا. أفترض أنها تقوم بذلك أثناء التنفيذ، ولهذا السبب لاحظت زيادة استخدام الذاكرة لكل عملية على مدار 3 أيام. لقد وصلت إلى حوالي 2 - 2.5 جيجابايت من الذاكرة لكل منها قبل فقدان اتصال قاعدة البيانات.

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

أعتقد ذلك. ولن تعمل العمليات الأخرى بشكل صحيح لأنها لا تحتوي على تلك الأيام التي استوردتها عمليات أخرى. لا أعتقد أن ما تفعله سيعمل.

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

احتمال قضاء أسابيع أكثر في تصحيح أكوادك بدلاً من مجرد الانتظار هو احتمال كبير. سرعة المعالج للعملية الواحدة هي أفضل طريقة لتسريع الأمور.

لم ألاحظ أي مشاكل مع هذا النهج حتى الآن، رغم أنني أتوقع حدوث بعض المشاكل مع بعض الطرق التي تُنفَّذ بعد استيراد المنشورات. ربما سأحتاج إلى التأكد من توقف كل شيء بعد ذلك، ثم تشغيل التنفيذ بخيط واحد (single-threaded) لضمان معالجة الباقي بشكل نظيف.

مع ذلك، فإن اقتراحك بمعالجة روابط معرف الاستيراد (import ID links) بشكل مختلف يبدو جيدًا في أي حال. الاحتفاظ بكمية كبيرة عشوائية من البيانات في بعض المتغيرات طوال مدة السكربت ليس فعالًا من حيث الكفاءة.