تحسين سرعة الترحيل من phpBB إلى Discourse

مرحباً، أنا أستخدم phpBB منذ عام 2004، وأقوم الآن بنقل منتدى بحجم لائق. حالياً، بعد يومين من العمل، تم نقل 303,139 من أصل 2,167,314 عنصرًا (14%)، بمعدل 446 عنصرًا في الدقيقة. كيف يمكنني تسريع العملية؟ أنا أستخدم Digital Ocean ولا أستخدم سوى 4% من وحدة المعالجة المركزية. كما أن Redis يتوقف عن الاستجابة بشكل متكرر. لقد قمت بتعطيل المرفقات.

بعض التفاصيل المفيدة ستساعد في تضييق نطاق المشكلة:

  • حجم الـ Droplet: الذاكرة العشوائية (RAM)، عدد وحدات المعالجة المركزية الافتراضية (vCPU)، نوع/حجم القرص
  • أمر الاستيراد الدقيق الذي تقوم بتشغيله
  • إعدادات الموارد في ملف app.yml، مع حذف الأسرار، ولا سيما:
    • UNICORN_WORKERS
    • db_shared_buffers
    • db_work_mem
  • ما إذا كان الاستيراد يعمل داخل حاوية Docker القياسية لـ Discourse
  • أي رسائل انتهاء مهلة لـ Redis أو مقتطفات من السجلات
  • ما إذا كانت قاعدة البيانات محلية داخل نفس الحاوية أم خارجية

ملاحظة هامة: من المرجح أن لا يسرع UNICORN_WORKERS عملية الاستيراد بشكل كبير، لأن مستورد phpBB لا يخدم بشكل أساسي عبر عمال الويب. قد يؤدي زيادته فقط إلى استهلاك ذاكرة عشوائية (RAM) أكثر. من المرجح أن تكون إعدادات ذاكرة PostgreSQL وإدخال/إخراج القرص (disk I/O) هي الأكثر أهمية.

بما أن استخدام المعالج المركزي (CPU) يبلغ حوالي 4% فقط، فقد تكون المشكلة مرتبطة بإدخال/إخراج القرص، أو بقاعدة البيانات، أو بانتظار استجابة Redis، وليس بنقص في قدرة المعالج. يستحق استكشاف انتهاء مهلة Redis بشكل منفصل.

image

سجلي المُحدَّث
script/import_scripts/phpbb3.rb:15:in ‘’
302570 / 2167314 ( 14.0%) [448 عنصر/دقيقة] استثناء أثناء إنشاء المنشور 304683. تم التجاوز.
تم الانتظار لمدة 1.0 ثانية
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/buffered_io.rb:214:in ‘block in RedisClient::RubyConnection::BufferedIO#fill_buffer’
internal:kernel:168:in ‘Kernel#loop’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/buffered_io.rb:197:in ‘RedisClient::RubyConnection::BufferedIO#fill_buffer’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/buffered_io.rb:187:in ‘RedisClient::RubyConnection::BufferedIO#ensure_remaining’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/buffered_io.rb:152:in ‘RedisClient::RubyConnection::BufferedIO#getbyte’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/resp3.rb:113:in ‘RedisClient::RESP3.parse’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection/resp3.rb:50:in ‘RedisClient::RESP3.load’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/ruby_connection.rb:97:in ‘RedisClient::RubyConnection#read’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/connection_mixin.rb:37:in ‘RedisClient::ConnectionMixin#call’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client.rb:374:in ‘block (2 levels) in RedisClient#call_v’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client/middlewares.rb:16:in ‘RedisClient::BasicMiddleware#call’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client.rb:373:in ‘block in RedisClient#call_v’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client.rb:781:in ‘RedisClient#ensure_connected’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-client-0.28.0/lib/redis_client.rb:372:in ‘RedisClient#call_v’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis/client.rb:90:in ‘Redis::Client#call_v’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/rack-mini-profiler-4.0.1/lib/mini_profiler/profiling_methods.rb:90:in ‘block in Redis::Client#profile_method’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis.rb:152:in ‘block in Redis#send_command’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis.rb:151:in ‘Monitor#synchronize’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis.rb:151:in ‘Redis#send_command’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis/commands/scripting.rb:110:in ‘Redis::Commands::Scripting#_eval’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/redis-5.4.0/lib/redis/commands/scripting.rb:97:in ‘Redis::Commands::Scripting#evalsha’
/var/www/discourse/lib/discourse_redis.rb:38:in ‘Kernel#public_send’
/var/www/discourse/lib/discourse_redis.rb:38:in ‘block in DiscourseRedis#method_missing’
/var/www/discourse/lib/discourse_redis.rb:29:in ‘DiscourseRedis.ignore_readonly’
/var/www/discourse/lib/discourse_redis.rb:38:in ‘DiscourseRedis#method_missing’
/var/www/discourse/lib/discourse_redis.rb:270:in ‘DiscourseRedis::EvalHelper#eval’
/var/www/discourse/lib/distributed_mutex.rb:82:in ‘DistributedMutex#get_lock’
/var/www/discourse/lib/distributed_mutex.rb:50:in ‘block in DistributedMutex#synchronize’
/var/www/discourse/lib/distributed_mutex.rb:49:in ‘Thread::Mutex#synchronize’
/var/www/discourse/lib/distributed_mutex.rb:49:in ‘DistributedMutex#synchronize’
/var/www/discourse/lib/distributed_mutex.rb:34:in ‘DistributedMutex.synchronize’
/var/www/discourse/lib/post_creator.rb:410:in ‘PostCreator#transaction’
/var/www/discourse/lib/post_creator.rb:200:in ‘PostCreator#create’
/var/www/discourse/script/import_scripts/base.rb:611:in ‘ImportScripts::Base#create_post’
/var/www/discourse/script/import_scripts/base.rb:555:in ‘block in ImportScripts::Base#create_posts’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/alias_method.rb:8:in ‘Mysql2::Result#each’
/var/www/discourse/vendor/bundle/ruby/3.4.0/gems/rack-mini-profiler-4.0.1/lib/patches/db/mysql2/alias_method.rb:8:in ‘Mysql2::Result#each’
/var/www/discourse/script/import_scripts/base.rb:542:in ‘ImportScripts::Base#create_posts’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:212:in ‘block in ImportScripts::PhpBB3::Importer#import_posts’
/var/www/discourse/script/import_scripts/base.rb:943:in ‘block in ImportScripts::Base#batches’
internal:kernel:168:in ‘Kernel#loop’
/var/www/discourse/script/import_scripts/base.rb:942:in ‘ImportScripts::Base#batches’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:293:in ‘ImportScripts::PhpBB3::Importer#batches’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:208:in ‘ImportScripts::PhpBB3::Importer#import_posts’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:38:in ‘ImportScripts::PhpBB3::Importer#execute’
/var/www/discourse/script/import_scripts/base.rb:47:in ‘ImportScripts::Base#perform’
/var/www/discourse/script/import_scripts/phpbb3/importer.rb:22:in ‘ImportScripts::PhpBB3::Importer#perform’
script/import_scripts/phpbb3.rb:35:in ‘module:PhpBB3
script/import_scripts/phpbb3.rb:16:in ‘module:ImportScripts
script/import_scripts/phpbb3.rb:15:in ‘’
333919 / 2167314 ( 15.4%) [441 عنصر/دقيقة]

يبدو أنه يعمل على ما يرام، ولا توجد شكاوى حتى الآن.

تم إيقاف يونيكورن.

تم تعطيل المرفقات والصور الرمزية.

يبدو أن حجم القطرة قوي بما يكفي لهذا النوع من الاستيراد، لذا لا أتوقع أن يكون UNICORN_WORKERS هو المحدد الرئيسي هنا.

بعض الملاحظات:

  • إيقاف يونيكورن قد يكون مقبولًا لحاوية مخصصة للاستيراد فقط أو للتجربة، لكنه على الأرجح لن يسرّع عملية الاستيراد نفسها بشكل كبير.
  • تعطيل المرفقات والصور الشخصية يجب أن يزيل أحد أبطأ الأجزاء، لذا إذا كنت لا تزال تحصل على حوالي 440 منشورًا في الدقيقة فقط، فقد يكون الاختناق في إدخال/إخراج قاعدة البيانات، أو جانب استعلام phpBB MySQL في المصدر، أو انتظار Redis/PostgreSQL.
  • يستحق سطر السجل المراقبة الدقيقة:

Exception while creating post 304683. Skipping.

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

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

من الخطوات التالية المفيدة:

free -h
df -h
iostat -xz 1
vmstat 1
redis-cli INFO memory
redis-cli INFO stats
redis-cli SLOWLOG GET 20

أيضًا، هل قاعدة بيانات phpBB MySQL محلية على نفس القطرة، أم يتم قراءتها عبر الشبكة؟ نظرًا لأن تتبع المكدس يتجاوز mysql2، فإن قاعدة بيانات المصدر البطيئة أو القرص البطيء يمكن أن تبقي استخدام المعالج منخفضًا بينما ينتظر المستورد.

بالنسبة للمعدل الحالي، من 333919 / 2167314 بحوالي 441 عنصرًا في الدقيقة، لا يزال لديك حوالي 69 ساعة متبقية، لذا قد ينتهي الأمر، لكنني سأكون قلقًا بشكل أساسي من أن استثناءات مهلة Redis هذه قد تتسبب في تخطي بعض المنشورات.

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