لما البوت تبعي صار “يختنق”: قصة من أرض الواقع
قبل فترة، كنت قاعد ببرمج على مشروع جديد، وفنجان القهوة جنبي، والأمور “عال العال”. فجأة، بدأت توصلني رسايل على تيليجرام من المستخدمين: “يا أبو عمر، البوت معلّق!”، “أبو عمر، البوت بطيء كتير اليوم، شو القصة؟”. في البداية، قلت يمكن ضغط مؤقت على السيرفر، أو مشكلة بالإنترنت عند الشباب.
لكن الرسائل استمرت، والبوت فعلًا كان يستجيب ببطء شديد، كأنه واحد “صايم وتعبان بآخر يوم رمضان”. ما كان بدها تفكير، فتحت الـ SSH على السيرفر فورًا وكتبت الأمر اللي كل مطور بخاف من نتيجته: df -h. الصدمة كانت إن الهارد ديسك مستهلك بنسبة 98%! كيف هيك؟ السيرفر جديد نسبيًا والمشروع مش ضخم لهالدرجة.
بدأت رحلة البحث والتحري، ودخلت على مجلدات النظام واحد ورا الثاني. وبعد شوية حفر باستخدام الأمر du -sh *، اكتشفت المصيبة: قاعدة بيانات MongoDB، وتحديدًا الـ collection اللي بخزّن فيها سجلات الأنشطة (Logs)، حجمها وصل عشرات الجيجات. البوت كان حرفيًا بيختنق بتاريخه، كل حركة وكل أمر وكل خطأ صغير سجله على مدار شهور، تراكم فوق بعضه لحد ما خنق السيرفر كله.
هون أدركت إن الحل مش بس أحذف السجلات القديمة، الحل لازم يكون جذري ودائم. لازم أبني نظام ذكي ينظّف ورا حاله أول بأول. ومن هاي التجربة، ولدت فكرة ورشة العمل الآلية اللي راح أشاركها معكم اليوم.
المشكلة بالتفصيل: ليش قاعدة البيانات “بتسمن” هيك؟
قبل ما ندخل في الحل، خلينا نفهم أصل المشكلة. ليش قواعد البيانات، وخصوصًا اللي بتتعامل مع تطبيقات تفاعلية مثل البوتات، بتكبر بسرعة؟
تراكم السجلات (Logs)
إحنا كمبرمجين بنحب نسجل كل شي! وهذا إشي صحي ومفيد للأسباب التالية:
- تصحيح الأخطاء (Debugging): لما يصير خطأ، أول مكان بنركض عليه هو السجلات لنفهم شو اللي صار.
- تحليل سلوك المستخدم: بنعرف شو أكثر الأوامر استخدامًا، وكيف بتفاعل المستخدم مع البوت، وهذا بيساعدنا نحسّن التجربة.
– الأمان والمراقبة: بنسجل محاولات الوصول غير المصرح بها أو أي نشاط غريب.
المشكلة إنه هاي البيانات، مع أهميتها في لحظتها، بتفقد قيمتها التشغيلية مع مرور الوقت. سجل نشاط من 6 شهور نادرًا ما تحتاجه بشكل فوري، لكنه بضل ماخذ مساحة وبطيء عمليات البحث في قاعدة البيانات.
تأثير الأداء
لما الـ collection في MongoDB يكبر كثيرًا، بتبدأ المشاكل تظهر:
- بطء الاستعلامات (Queries): كل ما زاد عدد المستندات (Documents)، كل ما أخذت قاعدة البيانات وقت أطول لتبحث عن المعلومة اللي بدك إياها، حتى مع وجود الفهرسة (Indexing).
- استهلاك الذاكرة (RAM): الفهارس نفسها بتكبر وبتاخذ مساحة أكبر من الذاكرة، وهذا بيأثر على أداء السيرفر بشكل عام.
- عمليات النسخ الاحتياطي: أخذ نسخة احتياطية من قاعدة بيانات حجمها 50 جيجا بايت مش زي لما يكون حجمها 500 ميجا بايت. العملية بتصير أبطأ وبتستهلك موارد أكثر.
الحل اليدوي مقابل الحل الآلي: ليش الأتمتة هي الخيار الأمثل؟
أول حل خطر ببالي كان بسيطًا: أدخل على السيرفر كل شهر وأشغل سكربت يحذف البيانات الأقدم من 3 شهور. لكن هذا الحل “اليدوي” فيه مشاكل كثيرة:
- النسيان: أنا وأنت بشر، ومع كثرة المشاريع والمهام، أكيد رح ننسى نعمل هاي العملية في يوم من الأيام.
- الخطأ البشري: ممكن في عجلة من أمرك تشغل الأمر الخطأ وتحذف بيانات مهمة.
- فقدان البيانات: الحذف يعني أن البيانات راحت للأبد. ماذا لو احتجتها بعد 5 شهور لعمل تقرير معين؟
هنا يأتي دور الأتمتة. الحل المثالي هو بناء عملية مؤتمتة (Automated Workflow) تعمل في الخلفية بدون أي تدخل مني. هاي العملية مش بس بتحذف، بل بتقوم بعملية أذكى: الأرشفة ثم الحذف. يعني بننقل البيانات القديمة لمكان آمن ورخيص (بنسميه Cold Storage) وبعدين بنحذفها من قاعدة البيانات الأساسية (اللي بنسميها Hot Storage).
بناء ورشة العمل الآلية خطوة بخطوة (Workflow)
لأتمتة هاي العملية، أنا من محبين استخدام أدوات مثل n8n (أو بدائل مثل Zapier أو Make). هاي الأدوات بتخليك تبني عمليات معقدة عن طريق واجهة رسومية سهلة، كأنك بتركب قطع ليغو.
ورشة العمل تبعتنا راح تتكون من 4 خطوات أساسية:
مكونات الورشة:
- أداة الأتمتة: n8n (يمكنك استضافته بنفسك مجانًا).
- قاعدة البيانات: MongoDB.
- مخزن الأرشفة (Cold Storage): Google Drive (سهل ومجاني للبداية) أو Amazon S3 (أكثر احترافية).
الخطوة الأولى: الجدولة (Schedule Node)
كل ورشة عمل لازم يكون إلها “زناد” أو Trigger يشغلها. في حالتنا، الزناد هو الوقت. رح نستخدم “Schedule Node” في n8n ونضبطه إنه يشتغل مرة كل شهر، مثلاً في أول يوم من كل شهر الساعة 3 الفجر، وقت يكون الضغط على السيرفر قليل.
هذا يضمن أن عملية التنظيف تتم بشكل دوري ومنتظم بدون ما أفكر فيها حتى.
الخطوة الثانية: استخراج البيانات القديمة من MongoDB
الآن، بدنا نخاطب قاعدة البيانات ونطلب منها تعطينا كل السجلات اللي عمرها أكبر من 3 شهور. رح نستخدم “MongoDB Node” لهذا الغرض.
الاستعلام (Query) اللي رح نكتبه داخل الـ Node رح يكون شبيه بهذا:
{
"createdAt": {
"$lt": "{{ $now.minus({months: 3}).toISODate() }}"
}
}
شرح الكود:
"createdAt": هذا هو اسم الحقل في قاعدة البيانات اللي بخزن تاريخ إنشاء السجل. تأكد من استخدام الاسم الصحيح عندك."$lt": هذا عامل التشغيل في MongoDB ويعني “أقل من” (Less Than)."{{ $now.minus({months: 3}).toISODate() }}": هذا هو سحر n8n. هاي عبارة برمجية (Expression) معناها: “أعطني التاريخ والوقت الحالي ($now)، اطرح منه 3 شهور (.minus({months: 3}))، وحوّله لصيغة تاريخ تفهمها MongoDB (.toISODate())”.
نتيجة هاي الخطوة هي قائمة بكل المستندات القديمة اللي بدنا نأرشفها.
الخطوة الثالثة: الأرشفة في “مخزن بارد” (Google Drive)
البيانات اللي حصلنا عليها في الخطوة السابقة ما بدنا نحذفها مباشرة. بدنا نحفظها في مكان آمن. رح نستخدم “Google Drive Node” لهذا الغرض.
في إعدادات هذا الـ Node، رح نعمل الآتي:
- تحديد العملية: نختار “Upload File”.
- تحديد نوع البيانات: نطلب منه ياخذ البيانات من خطوة MongoDB السابقة ويحولها لملف. الخيار الأفضل هو JSON لأنه بحافظ على بنية البيانات الأصلية.
- تسمية الملف: ما بدنا كل مرة الملف يكون بنفس الاسم. رح نستخدم Expressions مرة ثانية لنعطيه اسم ديناميكي، مثل:
logs-archive-{{ $now.toFormat('yyyy-MM') }}.json. هذا رح ينتج ملفات بأسماء مثلlogs-archive-2024-10.json. - تحديد المجلد: نختار مجلد معين في Google Drive اسمه “Archives” مثلاً عشان نحافظ على الترتيب.
لما هاي الخطوة تنتهي بنجاح، بكون عنا نسخة آمنة من بياناتنا القديمة في مكان خارجي.
الخطوة الرابعة: الحذف الآمن من MongoDB
هذه أخطر خطوة، ولازم نعملها صح. ما رح نحذف أي شيء إلا بعد التأكد 100% إن عملية الأرشفة في Google Drive نجحت. جمال أدوات مثل n8n إنها بتنفذ الخطوات بالتسلسل، فهاي الخطوة ما رح تشتغل إلا لو نجحت اللي قبلها.
رح نضيف “MongoDB Node” ثاني، لكن هذه المرة رح نختار عملية “Delete”.
السؤال الآن، كيف نخبره أي مستندات يحذف؟ الجواب بسيط: رح نستخدم الـ IDs (المعرفات الفريدة) للمستندات اللي حصلنا عليها في الخطوة الثانية.
الاستعلام رح يكون كالتالي:
{
"_id": {
"$in": "{{ $json.items.map(item => item.json._id) }}"
}
}
شرح الكود المعقد شوي:
"_id": حقل المعرّف الفريد في MongoDB."$in": عامل تشغيل يعني “موجود ضمن القائمة التالية”."{{ $json.items.map(...) }}": هاي Expression ثانية. معناها: “اذهب إلى البيانات القادمة من العقدة السابقة ($json.items)، وقم بإنشاء مصفوفة جديدة تحتوي فقط على قيمة_idمن كل عنصر”.
بهذه الطريقة، نحن نضمن أننا نحذف فقط وفقط المستندات التي تم استخراجها وأرشفتها بنجاح، لا أكثر ولا أقل.
نصائح من خبرة أبو عمر
بناء هذا النظام رائع، لكن قبل ما تشغله على بياناتك الحقيقية، خذ مني هاي النصائح من أرض الميدان:
- 💡 ابدأ صغيرًا وجرّب: لا تبدأ بـ 3 شهور مباشرة. جرب الـ Workflow على قاعدة بيانات تجريبية (Staging) واضبطه ليأرشف ويحذف البيانات الأقدم من يوم واحد فقط. راقب النتائج وتأكد أن كل شيء يعمل كما هو متوقع.
- 🛡️ النسخ الاحتياطي أولًا وأخيرًا: هذا النظام للأرشفة، وليس بديلًا عن استراتيجية النسخ الاحتياطي (Backup) الشاملة لقاعدة بياناتك. تأكد أن لديك نسخ احتياطية دورية محفوظة في مكان آمن.
- 🔔 المراقبة والإشعارات: أضف خطوة أخيرة في الـ Workflow تبعك (مثلاً “Telegram Node” أو “Email Node”) لترسل لك إشعارًا عند نجاح العملية أو فشلها. رسالة بسيطة مثل “تمت أرشفة سجلات شهر أكتوبر بنجاح” بتعطيك راحة بال لا توصف.
- ⚖️ اختر المخزن البارد المناسب: Google Drive ممتاز للبداية والمشاريع الصغيرة. لكن لو كبر مشروعك، فكر في حلول مثل Amazon S3 Glacier أو Azure Blob Storage Archive Tier. هذه الحلول مصممة للأرشفة طويلة الأمد وتكلفتها أقل بكثير على الأحجام الكبيرة.
الخلاصة: خلي بوتاتك وأنظمتك تتنفس بحرية 🌬️
مشكلة امتلاء قاعدة البيانات بالسجلات القديمة هي مشكلة شائعة جدًا، لكنها من النوع الصامت اللي ما بننتبه له إلا لما يسبب كارثة. بناء ورشة عمل مؤتمتة مثل التي شرحتها ليس مجرد حل تقني، بل هو استثمار في استقرار نظامك وراحة بالك كمطور.
بدلًا من أن تستيقظ يومًا على رسائل غاضبة من المستخدمين، يمكنك أن تطمئن بأن لديك مساعدًا آليًا يعمل في الخلفية، يحافظ على نظافة قاعدة بياناتك، ويضمن أن تطبيقك يعمل بسلاسة وسرعة. الأتمتة ليست رفاهية، بل هي ضرورة في عالم البرمجة الحديث.
يلا يا جماعة، شدوا حيلكم وخلينا نبني أنظمة ذكية بتريحنا وبتريح المستخدمين. وأي سؤال، أبو عمر حاضر!