أنا أواصل هذه المحادثة من ‘إعادة بناء HTML للموضوع بأكمله’ لأن تجاربي تتجه نحو اتجاه مختلف تمامًا، وقد اعتقدت أنه قد يكون هناك فائدة في مشاركة أفكاري ونتائجي أثناء تقدمي.
حالتي كالتالي: نحن على أعتاب إطلاق منتدى جديد تم ترحيله يحتوي على أكثر من 4 ملايين منشور. سيتطلب ذلك إعادة طهي (rebake) عند التبديل إلى النطاق النهائي، كما يجب معالجة المنشورات لضمان دمج الصور بشكل صحيح وما إلى ذلك.
تشمل مخاوفي ما يلي:
إعادة الطهي ليست عملية سريعة. قمت بضبط خادمنا المكون من 16 جيجابايت من الذاكرة و6 أنوية، لكنني لم أستطع تحقيق سرعة أكبر من 2-3 منشورات في الثانية، مما يعني أن إعادة الطهي الكاملة ستستغرق أكثر من 20 يومًا.
تبدأ عملية إعادة الطهي بأقدم المنشورات، وأفضل البدء بأحدثها لتوفير أفضل تجربة ممكنة لمجتمعنا (بافتراض أن أحدث المنشورات ستحصل على معظم الزيارات).
لا توجد طريقة لـ ‘استئناف’ العملية من حيث توقفت، ولدي أسباب لأشتبه في أنني سأحتاج إلى إعادة البناء مرة واحدة على الأقل خلال الـ 20 يومًا القادمة.
تذهب مهام إعادة الطهي إلى طابور Sidekiq الافتراضي، وأقلق من أن هذا سيخلق تأخيرات هائلة لمهام المعالجة العادية.
حتى الآن، قمت بما يلي: بعد البحث في الكود والحصول على بعض المساعدة من الموظفين هنا، قمت باختراق ملف lib/tasks/posts.rake لـ:
العمل بترتيب زمني عكسي، بدءًا من أحدث المنشورات.
تجاهل الرسائل الخاصة - أريد إعطاء الأولوية للمواضيع العامة أولاً.
إخراج معرف المنشور/الموضوع الحالي حتى أتمكن من إضافة شرط إلى جملة where في استعلامي لاستئناف المعالجة عند رقم منشور آخر.
إليك الكود الخاص بي:
def rebake_posts(opts = {})
puts "NEW Rebaking post markdown for '#{RailsMultisite::ConnectionManagement.current_db}'"
disable_edit_notifications = SiteSetting.disable_edit_notifications
SiteSetting.disable_edit_notifications = true
total = Post.count
rebaked = 0
ordered_post_ids = Post.joins(:topic)
.select('posts.id')
.where('topics.archetype' => Archetype.default)
.order("posts.id DESC")
.pluck(:id)
ordered_post_ids.in_groups_of(1000).each do |post_ids|
posts = Post.order(created_at: :desc).where(id:post_ids)
posts.each do |post|
rebake_post(post, opts)
print_status(rebaked += 1, total)
puts " > rebaking post id #{post.id} for topic id #{post.topic_id}"
end
end
SiteSetting.disable_edit_notifications = disable_edit_notifications
puts "", "#{rebaked} posts done!", "-" * 50
end
الخطوة التالية: أعمل على figuring out كيفية إنشاء هذه المهام في طابور الأولوية المنخفضة. أي تلميحات ستكون موضع ترحيب كبير
Now I’ve started my first large test, I noticed that the jobs processing has made several huge ‘steps’ in speed. I suspect this may have to do with a large number of my attached images having been moved to the tombstone - this is another ongoing project.
Yes I learned that too With the help of your team I figured out how to work around it though. I’m not sure this is a smart or even fast way of doing it, but it works for me.
Next issue: our new site will already go live while the posts:rebake job is running. Will having a large number of jobs in the default queue slow down regular site processes, and should I try to have posts:rebake start its jobs in the low priority queue instead? Or is this automatically handled?
So far, it seems that the queue that a job will be created in is a property of the job’s class, I’m not sure I could influence this in some way from within the posts.rake script?
If not, I’ll throttle the creation of new jobs to make sure the queue isn’t filling up.
I think there’s also a ‘version’ column on the posts table that you can null out to cause gradual rebaking, too. I think it does 100 posts every time the job triggers.
So rather than running rake posts:rebake, one should instead do Posts.all.update_all('baked_version: null') and all posts will be rebaked in batches according to rebake_old_posts_count?
Agree, but it is a bit tricky cause we would need to carry a big list of ids in memory. I wonder if we should amend it so the rake task is resumable?
Have rake posts:rebake reset version and just work through old posts using calls to rebake_old
And add rake posts:rebake:resume that simply resumes an interrupted rebake.
Downside here is that posts:rebake would unconditionally cause posts to rebake at some point in time even if the task is interrupted, but this may not matter.
we can probably live with it to be honest … that retains the tasks working exactly as they do today (in reverse order). Though something in me wants these tasks to be resumable cause if you are working through 20 million posts this can take many hours and if it breaks half way through it can be very frustrating to start from scratch.
I’ve used a script that was resumable at the topic level by using the custom fields. Here’s one that skips private messages (since my import had a LOT of them and they weren’t a priority):
Topic.includes(:_custom_fields).where(archetype: Archetype.default).find_each do |t|
unless t.custom_fields["import_rebake"].present?
t.posts.select(:id).find_each do |post|
Jobs.enqueue(:process_post, {post_id: post.id, bypass_bump: true, cook: true})
end
t.custom_fields["import_rebake"] = Time.zone.now
t.save
end
end
(This filled up Sidekiq’s default queue, so it’s not useful if you want to launch your site before the rebakes are completed.)
After they’re all done, all the TopicCustomField records with name “import_rebake” can be deleted.