VOL52: How Slack Handles Billions of Tasks in Milliseconds
أهلًا وسهلا بكم في العدد الثاني والخمسين من النشرة الأسبوعية لاقرأ-تِك 🚀
لا تنسوا أهلنا من صالح الدعاء,اللهم إنّا استودعناك اياهم، اللهم كُن عوناً لهم، اللهم انصرهم واحفظهم. 🇵🇸
أهلًا وسهلا بكم في العدد الثاني والخمسين من النشرة الأسبوعية لاقرأ-تِك 🚀
سواء كنت مهندس برمجيات مبتدئ أو محترف، فنشرتنا هدفها انها تثري المحتوى التقني العربي سعيا للتطوير من جودة المحتوى باللغة العربية، من خلال تقديم أحدث المستجدات والتطورات في عالم البرمجيات، بالإضافة إلى أفضل الممارسات والنصائح القيمة، ونشر أحدث المقالات وترشيحات الكتب ومحتوى ورقة وقلم اللي بينزلوا بشكل مستمر في موقع اقرأ-تِك.
في الإصدار ده الفهرس هيكون كالآتي:
How Slack Handles Billions of Tasks in Milliseconds
Observability In a Nutshell
How Keyed Watermarks Enhance Apache Flink’s Accuracy
Database Connection Pool
Introduction Into Robots.txt
How Slack Handles Billions of Tasks in Milliseconds
لو جينا نبص على Slack هنلاقيها بتستخدم job queue system علشان تنفّذ الـ business logic اللي بياخد وقت كبير ومينفعش يتنفذ جوه الـ web request الطبيعي. والسيستم ده جزء أساسي في البنية التحتية بتاعت Slack، وكمان بيتم استعماله في كل message بتتبعت، وكل push notification، وكل URL unfurl، والتذكيرات، وحساب الفواتير وغيرها كتير من المميزات اللي Slack بتقدمها.
في أغلب الأيام اللي بيكون عليها ضغط كبير، السيستم بيعالج أكتر من 1.4 مليار job، بمعدل بيوصل لـ 33,000 job في الثانية. وده رقم ضخم جدًا ومع ذلك تنفيذ الـ jobs بياخد من ميلي ثانية لحد دقايق أحيانًا.
الـ job queue القديم اللي كانوا بيستخدموه من أول ما Slack بدأت، قدر يمشي معاهم الرحلة دي وهم بيكبروا. وكل ما كانوا بيقابلوا مشاكل في الـ CPU أو الـ memory أو الـ Network، كانوا بيعملوا scaling، لكن الـ Architecture الأساسي للـ System فضل زي ما هو تقريبًا.
من حوالي سنة، حصل عندهم production outage كبيرة بسبب الـ job queue. كان فيه مشكلة في الـ database layer، خلت تنفيذ الـ jobs يبقى بطيء جدًا، وده خلى Redis يوصل لأقصى سعة من الـ memory الممكنة. ولما مابقاش فيه memory فاضية، مقدروش يحطوا jobs جديدة، وبالتالي كل حاجة كانت معتمدة على الـ job queue وقعت.
اللي زوّد الطين بلة إن حتى عملية الـ dequeue كانت محتاجة memory، فكده الموضوع كان بايظ من الناحيتين لا عارفين يزودوا jobs جديدة ولا حتى يعملوا dequeue للـ jobs الموجودة بسبب ان مفيش free memory ، فحتى بعد ما مشكلة الـ database اتحلت، الـ queue فضلت واقفة، واحتاجت تدخل يدوي عشان تشتغل تاني.
الـ incident دي خلتهم يعيدوا تقييم الـ job queue system كله، وبدأوا يخططوا لتغيير كبير في التصميم، لكن من غير ما يوقفوا أي service، أو يعملوا migration ضخم، وكمان يوفروا سبيل لأي تحسينات مستقبلية ممكن يضيفوها بعدين.
Slack's Initial Job Queue System Design
الـ system design القديم بتاع الـ job queue عند Slack كان ممكن ترسمه بالشكل ده، وغالبًا هيكون مألوف لأي حد اشتغل أو استخدم Redis task queue قبل كده.
Job's Lifecycle
لما الـ web app بيعمل enqueue لـ job: أول خطوة بتكون إنه ينشئ identifier (وده unique identifier) مبني على نوع الـ job والـ arguments اللي جاية معاها.
الـ enqueue handler بيختار واحد من Redis hosts: والاختيار بيتم بناءً على استعمال hash من الـ identifier ده وكمان الـ logical queue اللي تخص الـ job دي ، يعني كل job بتروح على Redis معين حسب نوعها ومحتواها.
في Redis، بيستخدموا data structures علشان يعملوا deduplication وبيكون limited شوية: فلو فيه job بنفس الـ ID أصلًا موجودة جوه الـ queue الطلب الجديد بيتلغى. ولو مفيش واحدة زيها الـ job بتضاف للـ queue.
الـ Workers دايمًا بتعمل polling على Redis clusters: في الـ background، بيكون فيه pools من الـ (worker machines) متابعة وبتراقب الـ queues، ومستنية يظهر أي job جديدة عشان تشتغل عليها. أول ما واحدة من الـ workers تلاقي job في queue من اللي هي بتراقبهم:
بتنقلها من pending queue لـ in-flight list (يعني الشغل اللي شغال عليه دلوقتي).
بتبدأ تشغّل (asynchronous task) علشان تنفّذ الـ job دي.
بعد ما الـ task تخلص: الـ worker بيشيل الـ job من قائمة in-flight jobs ولو الـ job فشلت وحصلها (failure) بتتحط في queue خاص بإعادة المحاولة (retry queue). وبيتم إعادة المحاولة عدد معين من المرات، لحد ما يا إما تنجح ، يا إما تتحط في قائمة تانية اسمها permanently failed jobs (اللي بيتم مراجعتها يدويًا علشان يشوفوا هي فشلت ليه، ويحلوها لو محتاجة تدخل بشري).
Architectural Challenges
بعد ما عملوا post-mortem للـ outage اللي حصل، Slack وصلت لاستنتاج واضح:
"الـ scaling بالنظام الحالي مش هينفع، ولازم نشتغل على تغييرات جذرية أكتر."
طب إيه هي المشاكل والقيود اللي اكتشفوها؟
مبدئيًا كده Redis كان مفيهوش مساحة كفاية، خصوصًا من ناحية الـ memory: فلو عدد الـ jobs اللي بتتعمل enqueue بقى أسرع من الـ dequeue لفترة طويلة، السيستم بيستهلك الذاكرة كلها. ولما الـ memory تخلص، حتى dequeueing ميبقاش ممكن (لأنه هو كمان بيحتاج memory علشان يحرك الـ job لقايمة الـ processing).
الـ Redis connections كانت عاملة complete bipartite graph: كل client (أي system بيعمل enqueue) لازم يكون متصل بـ كل Redis instance. وده معناه إن كل client لازم يعرف كويس كل تفاصيل Redis nodes، ويكون عنده config مظبوط ومحدّث دايمًا.
الـ Job workers ماكانوش يقدروا يعملوا scaling لوحدهم من غير Redis: كل ما بنزود عدد الـ workers بنحط load أكتر على Redis.
الاختيارات اللي كانوا عاملينها قبل كده في الـ Redis data structures كانت مكلفة: علشان نعمل dequeue من الـ queue، كان لازم نعمل عمليات تتناسب مع الـ queue length نفسه. أو بمعنى أبسط (كل ما الـ queue يطول، كل ما بيبقى أصعب إننا نفضيه).وده كان مخلي الأداء يبقى أسوأ مع الوقت.
الـ semantics والـ QoS (quality-of-service) guarantees اللي بتتقدم للـ app والـ platform engineers كانت مش واضحة: فالـ async job processing كان مفروض يكون حاجة أساسية في الـ architecture ، لكن في الواقع، المهندسين كانوا بيترددوا يستخدموه علشان سلوكه مكنش متوقع. فأي تغيير حتى ولو بسيط (زي موضوع deduplication المحدود) كان high-risk جدًا، لإن فيه jobs كتير بتعتمد عليه علشان تشتغل صح.
طب يعملوا إيه؟ إيه الحل؟
كل مشكلة من دول كانت ممكن تروح في كذا اتجاه من الحلول — من مجرد تحسينات بسيطة، لحد عملية الـ Re-Write للسيستم من الصفر. لكن Slack قرروا يركزوا على 3 نقاط رئيسية ممكن تحل المشاكل اللي ليها أولوية:
استبدال Redis بـ durable storage (زي Kafka): علشان يبقى فيه buffer يتحمّل ضغط الـ memory، وميفقدش الـ jobs لو حصل أي overload. وده لإن Kafka يقدر يحتفظ بالبيانات دي بأمان على الـ disk، بدل ما كل حاجة تبقى in-memory.
بناء scheduler جديد خاص بالـ jobs: ده هيساعدهم يقدّموا guarantees أفضل من ناحية QoS. وكمان يقردوا يوفّرا features بأريحية زي مثلًا الـ rate-limiting، والـ prioritization في تنفيذ jobs.
فصل تنفيذ الـ jobs عن Redis تمامًا: كده أصبح بإمكانهم إنهم يعملوا scale للـ job execution من غير ما يضطروا يتعاملوا مع Redis نفسه.
Observability In a Nutshell
كثيرًا ما نسمع عن ال Observability ولكن ما هي حقًا ، وما هي أهدافها وإزاي نستغلها لتوصيف وحل أي مشكلة في أي نظام؟!
ورقة وقلم وخلونا نبدأ بقصة طبية بسيطة تفهمنا كل حاجة عن الموضوع دا قبل ما ندخل في الكلام عنه.
توصيف الـ Observability في الحياة العملية
الإنسان لما بيتعب بيروح للطبيب اللي بدوره بيعمل عدة خطوات تساعده في تقييم الحالة وإيجاد العلاج المناسب:
بيقوم بأخذ مؤشرات الشخص الحيوية
بيسأل الشخص بعض الأسئلة عشان يقدر يوصف الأعراض
وبيقوم بعمل أشعة على الجسم لتحديد مكان الألم
جسم الإنسان عبارة عن نظام متكامل ولكن هو كذلك عبارة عن أجهزة مختلفة بتتعاون مع بعض زي الجهاز الهضمي والتنفسي وإلخ… والخطوات دي بتساعد الطبيب يعرف مصدر الشكوى ويحدد لها علاج مناسب وأساليب وقائية لمنع تكرارها.
أي Software في الدنيا زي الجسم بالظبط ، بيبقي نظام متكامل يتكون من عدة أنظمة أصغر.
هدف ال Observability الأساسي هو تحويل النظام من Blackbox إلى Glass Box.
أهم عناصر المراقبة (Pillars of Observability)
ولازم المبرمج يقوم بنفس خطوات التشخيص اللي قام بيها الطبيب عشان يحل أي مشكلة بتطرأ علي هذا النظام
How Keyed Watermarks Enhance Apache Flink’s Accuracy
المشكلة بدأت لما كنا بنحتاج إننا نعمل processing لعدد كبير من الـ events اللي بنستقبلها من sensors موجودة فى العربيات. بنستخدم ال events دي علشان نحلل سلوك معين للسواقين وكذلك ال rides.
فسيناريو زي ده أول حل ف دماغنا هيجي إننا نستخدم Apache Flink
علشان الـ case بتاعتنا streaming و Flink
أنسب حد في المهمة دي.
المعروف عن الـ Streaming Engines اللي زي Flink
انهم بيستخدموا technique معين علشان يعملوا tracking للـ events خصوصاً الـ event time وواحد من أشهر ال techniques دي هو الـ Watermarks.
Apache Flink's Watermarks
فالـ Stream Processing علشان نتأكد إن النتايجة بتاعتنا دقيقة كفاية ، يُفضل إننا نستخدم ال event time مش ال processing time لإن ال event time هو الوقت اللي الـ event فيه generated من الـ source وبالتالي لو حصل أي تأخير لأي سبب في وصول الـ event ده لل engine نقدر نستخدم الـ event time علشان نعرف ال accurate ordering بتاع الـ events اللي جاية وبالتالي نخلي النتايج بتاعتنا أكثر دقة.
الـ Watermarks فـ Flink بتشتغل بإنه بيحصل generation كل وقت معين (default: 200ms) لـ timestamp اسمها الـ Watermark عن طريقها الـ system بيقدر يحدد إنه خلاص انا مش محتاج يستني الـ events المتأخرة لإن كده خلاص عدى وقت كبير وللحفاظ على الـ latency إنها تفضل قليلة (واللي هو من مميزات Flink) الأفضل إننا دلوقتي نبدء processing للـ events الموجودة عندنا.
مشكلة الـ default watermarking mechanism الموجودة فـ Flink إنها بتشتغل على مستوى الـ stream كله ولإن الـ watermark بيحصلها generation بناءً على الـ timestamps بتاعت الـ events اللي بتجيلي ف هنا بتظهر المشكلة.
إنه قيمة الـ watermark بتكبر بسرعة فـ حالة مثلا إنه وصل الـ engines مجموعة من الـ events اللي الـ timestamp بتاعتها عالية واللي في نفس الوقت حصل تأخير لأي سبب لمجموعة تانية من الـ events اللي المفروض انهم كانوا يوصلوا الـ system الأول.
هنا كده حصل مشكلة ترتيب اللي بنقول عليها out-of-orderness.
الحل
كان الحل إننا ليه منعملش watermarking mechanism تكون بتشتغل على كل source من اللي بيبعت الـ events لوحده، بحيث لو في source لسبب ما بيوصل منه الـ events متأخرة ده هيكون له watermark value لوحده ولو في source بيبعت الـ events بسرعة هيكون نفس الكلام له ال watermark value بتاعته وبالتالي مفيش حد هيتأثر بالسلوك بتاع التاني وكل sub-stream هيشتغل لوحده.
وهنا كان بداية شغلنا على extension جديد للـ watermarking component فـ Apache Flink واللي سميناه Keyed Watermarks
خصم 50% على جميع خطط الاشتراك السنوية لفترة محدودة، تقدروا دلوقتي تشتركوا في اقرأ-تِك وتستمتعوا بكافة المقالات في كل ما يخص هندسة البرمجيات باللغة العربية والمحتوى المميز من ورقة وقلم ومدونات فطين اللي بيتميزوا بتصاميم ذات جودة عالية وكل ده بحرية كاملة وكمان مفاجآت اقرأ-تِك الجاية 🚀
وبرضو متاح الاشتراك من خلال InstaPay و VodafoneCash 🎁
بفضل الله أصبح متاح حاليا دعمنا من خلال الرعاة والشراكات وفعلنا الـ Sponsorship واحنا بنرحب بجميع الشراكات مع المؤسسات والشركات وأصحاب الأعمال لبناء مجتمع عربي يشجع على القراءة والتعلم ومشاركة التجارب والخبرات العملية في هندسة البرمجيات.
دورك كشريك أو راعي هيكون محوري في دعم المحتوى وتوسيع نطاق تأثيره. فانضم لرحلتنا وكن جزءًا من صناعة مستقبل التكنولوجيا في المنطقة 🚀
تقدروا تشوفوا التفاصيل كاملة من هنا والـ Analytics بتاعتنا من خلال اقرأ-تِك والنشرة الأسبوعية 👇
Database Connection Pool
تخيل تروح فرع لبنك او أي فرع لشركة تليفون عشان تستفسر عن حاجة معينة وتلاقي مفيش غير شباك واحد , وفيه مئات الناس مستنيين أدوارها تيجي , وعلى ما يجي دورك وتستفسر ومسئول الشباك يساعدك بتكون استهلكت وقت كبير جدًا.
طب هيكون ايه رد فعلك لو مع كل حد هيجي يسال عن حاجة معينة, الفرع هيروح يدور على حد يوظفه يقعد في الشباك يساعدك وبعدين يمشي الموظف ده ؟
هو ده كان هيبقى شكل حياة تطبيقاتنا من غير Database Connection Pool بكل بساطة
Database Connection Lifecycle
خلونا قبل ما نتكلم عن الـ Database Connection Pool , نعرف ايه اللي بيحصل اما بنعوز نتصل بقاعدة البيانات من خلال التطبيق بتاعنا :
التطبيقات بتستعمل Database Drivers عشان تفتح Connection مع قاعدة البيانات
بيتم فتح Network Socket عشان نوصل التطبيق بقاعدة البيانات
المستخدم بيتعمله Authentication وفي حالتنا هنا بيكون التطبيق
بعد ما العملية تتم بنجاح , ممكن الـ Connection يتقفل
ومن ثم الـ Network Socket بيتقفل هوا كمان
فزي ما احنا شايفين فتح وقفل الـ Connection مع قواعد البيانات عملية مش سهلة وبتضمن أكتر من خطوات بالإضافة لكونها مستهلكة للـ Resources.
وطبعًا هنلاحظ ان ده ممكن يكون عادي في لو التطبيقات كانت صغيرة , ولكن مع نمو وتطور التطبيقات بتاعتنا هيواجهنا مشكلة كبيرة بخصوص الموضوع ده .
ليه نستعمل Connection Pool ؟
الـ Database Connection Pool جه في الأساس عشان يعالج المشكلة اللي شوفناها بتاعة فرع البنك او شركة التليفون , وهو ان عشان تكلفة الـ Database Connections عالية وبتستهلك Resources فقام موفر أكتر من شباك لخدمة الناس .. أو بمعنى أصح مخزن أكتر من Database Connection جاهزين ..
وده عشان واول ما بيجي Request من التطبيق عشان يتواصل مع قاعدة البيانات نبدأ نعيد استعمال الـ Connections دي , بدل ما كل مرة نروح نفتح ونقفل Connection من أول وجديد ..
Introduction Into Robots.txt
لو عندك موقع إلكتروني، سواء كان كبير أو صغير، أكيد سمعت عن حاجة اسمها robots.txt، الملف ده صغير جدًا لكن ليه دور مهم في إنك تتحكم في طريقة تعامل محركات البحث مع موقعك، في المقال ده هشرحلك ببساطة إيه هو robots.txt، بيشتغل إزاي، وإزاي تستخدمه بطريقة صح علشان تحمي صفحاتك وتحسّن أداء موقعك على الإنترنت.
يعني إيه robots.txt
ده ملف نصي عادي بيتم وضعه في root path للموقع الخاص بيك، علي سبيل المثال مثلاً:
https://example.com/robots.txt
الملف ده وظيفته إنه يوجّه ال محركات البحث (زي Googlebot و Bingbot) ويقولهم الصفحات أو الملفات اللي ينفع يزوروها، واللي لأ.
شكل ملف robots.txt
الملف يكتب بصيغة بسيطة جدًا، تعال نشوف مثال:
User-agent: *
Disallow: /admin/
Allow: /public/
Sitemap: https://example.com/sitemap.xml
User-agent: *: معناها التعليمات دي لكل محركات البحث.
Disallow: /admin/: يعني البوتات ماتدخلش مجلد الأدمن.
Allow: /public/: يعني الجزء ده مفتوح ليهم.
Sitemap: https://example.com/sitemap.xml : معناها دا المكان لخريطه الموقع
وممكن تخصص التعليمات لبوت معين، زي Googlebot:
User-agent: Googlebot
Disallow: /private/
أهمية ملف robots.txt
في كذا سبب يخليك تحتاج تضيف ملف robots.txt لموقعك:
تحجب صفحات معينة: زي صفحة الأدمن أو لوحة التحكم، أو صفحات تحت التطوير.
تحافظ على أداء الموقع: بدل ما البوتات تزحف في كل الصفحات، تقدر من خلال ال robots.txt توجهها على الصفحات المهمة بس.
تقلل من الزحف على ملفات تقيلة: زي صور ضخمة أو ملفات تجريبية.
بيسهل الوصول ل sitemap الي بيمثل الخريطة للموقع الخاص بك، وعلاقه الصفحات ببعضها.
الإصدار الأول - ورقة وقلم 🚀
في الإصدار ده جمعنا أكتر من 50 موضوع في مختلف مجالات هندسة البرمجيات بأكتر من 170 صفحة + تصاميم بجودة عالية وكل ده بالعربي وبشكل مميز ومتقسم لفصول سهل تنتقلوا من فصل وموضع للتاني بدون مشاكل 💎
تقدروا تشوفوا النسخة كاملة من هنا كـ E-Book ، وحاولنا نخليها بسعر رمزي يناسب الجميع 👇
ولو عندكوا أي مشكلة في الدفع ، تقدروا تتواصلوا معانا وهنكون مبسوطين باننا نوفر بدايل زي InstaPay و VodafoneCash 🎁
ولو عاوزين تعاينوا جودة الـ E-Book قبل ما تشتروه ، تقدروا تحملوا النسخة المجانية واللي بتضم حوالي 30 موضوع فيما لايزيد عن 100 صفحة من هنا 😉
رؤيتنا هي إثراء المحتوى التقني العربي وجعل التعلم من خلال القراءة أمتع، وذلك من خلال إثراء المحتوى التقني باللغة العربية وتشجيع المبرمجين على القراءة بلغتهم الأم والتفكير أيضًا بها.
لذلك اتحنا الفرصة أمام الجميع للمساهمة ومساعدتنا في نشر واثراء المحتوى التقني باللغة العربية, من خلال كتابة المقالات التقنية في مختلف مجالات هندسة البرمجيات.
وجب التنويه أنه لن يتم نشر كافة الأعمال التي تصل إلينا، وإنما سيتم الانتقاء منها ما يحقق هدفنا بإثراء المحتوى التقني العربي، ولذلك قد تُطلب بعض التعديلات من الكاتب قبل النشر.
لمعرفة المزيد بخصوص :
💬 المعايير العامة لكتابة ونشر المقالات
⚡️ كيفية الإرسال
🔥 التزامات اقرأ-تِك تجاه الكتاب
يمكنكم قراءة كافة التفاصيل من هنا 👇