English· Español· Deutsch· Nederlands· Français· 日本語· ქართული· 繁體中文· 简体中文· Português· Русский· العربية· हिन्दी· Italiano· 한국어· Polski· Svenska· Türkçe· Українська· Tiếng Việt· Bahasa Indonesia

un

ضيف
1 / ?

الشكل 1: إصلاح الحالة. الشكل 2: تقرير مُهدر.

يخفق قلب مُقنَّن على ساعة. لا على الحاجة. ولا على التغيير. بل على مؤقت.

شكلان، سبب جذري واحد: وظيفة مجدولة تحل محل التصميم الصحيح.

الشكل 1: إصلاح الحالة

فشل انتقال حالة في الاكتمال الذري. بدلاً من إصلاح الانتقال، تعمل وظيفة خلفية بعد تأخير وتُصالح. يرى المستخدمون حالة معطوبة خلال نافذة المصالحة.

مثال من GitHub (2026-04-08): أصبح مستودع المصدر الرئيسي لطلب سحب (pull request) خاصًا. حاول GitHub إجراء انتقال حالة: إغلاق طلب السحب، تحديث حالة الفرع، ومسح حالة الدمج. لم يكتمل الانتقال بشكل ذري. أظهرت حالة طلب السحب في الوقت نفسه 'branch-forced-closed' و'Merge status cannot be loaded'. شغّلت وظيفة Sidekiq في الخلفية بعد دقائق وأكملت عملية التوفيق. رأى المراقبون حالة معطلة طوال مدة النافذة.

القلب المقنن: شغّلت وظيفة Sidekiq وفق جدول زمني. لم تشغّل لأن GitHub اكتشف حالة معطلة؛ بل شغّلت لأن المؤقت انطلق. رأى مستخدم يتابع طلب السحب في الوقت الفعلي طلب سحب يناقض نفسه حتى تنفيذ الوظيفة التالية.

النموذج 2: التقرير المُهدر

يقوم تقرير أو تجميع بإعادة الحساب من الصفر على فترة زمنية ثابتة. بدون فحص ذاكرة تخزين مؤقت. بدون حماية من التكرار. بدون تحديث تدريجي. كل تنفيذ: مسح كامل.

أمثلة: وظيفة cron ليلية تعيد حساب إجمالي مشتريات كل مستخدم بمسح جميع الطلبات منذ البداية. وظيفة تحليلات يومية تعيد إنشاء لوحة معلومات من سجلات الأحداث الخام. بريد ملخص أسبوعي يستعلم عن كل صف في جدول النشاط.

كل منها يُشغّل سواء تغيرت البيانات أم لا منذ التنفيذ السابق. وكل منها يمسح السجل الكامل حتى لو كانت آخر 24 ساعة فقط تحتوي على بيانات جديدة. وكل منها يستبدل التكرار المجدول بالتصميم التدريجي.

الجذر المشترك

لا يستطيع القلب المقنن أن يقول الحقيقة عن حالته الخاصة. فهو لا يعرف سوى الساعة. النموذج 1: وظيفة إصلاح الحالة تعمل عند T+5 دقائق بغض النظر عن كون الحالة معطلة عند T+0. النموذج 2: وظيفة التقرير تعمل عند الساعة 2 صباحًا بغض النظر عن تغير أي بيانات منذ الأمس.

لا تحمل الساعة أي معلومات حول ما يجب فعله. يحمل الحدث هذه المعلومات: "فشل انتقال حالة للتو"، "وصلت طلبات جديدة للتو". يتخلص Metered Heart من هذه المعلومات ويستبدلها بجدول زمني.

استنزاف رأس المال

يستنزف Metered Heart رأس المال الحي: المهندسون المتواجدون للتعامل مع حوادث الحالة المعطوبة. يُضعف الثقة الاجتماعية: يرى المستخدمون بيانات غير متسقة ويبلغون عن عيوب تختفي من تلقاء نفسها. يُضخّم MOADs الأخرى: مهمة إصلاح حالة تقوم بمسح جميع السجلات للعثور على الحالة المعطوبة غالبًا ما تحتوي على MOAD-0001 (مسح O(N²)). مهمة تقرير تعيد حساب البيانات الباردة قد تؤدي إلى MOAD-0005 (تدافع التخزين المؤقت). MOAD-0009 يفاقم العيوب الأخرى.

الجذر المشترك

يبدو Form 1 و Form 2 مختلفين على السطح: أحدهما يصلح الحالة، والآخر يعيد حساب البيانات. يربط السبب الجذري بينهما.

يشارك Form 1 و Form 2 سببًا جذريًا مشتركًا. صفه في جملة واحدة. ثم أعطِ مثالًا على كل شكل من برمجيات استخدمتها.

التشغيل عند التغيير، لا عند الساعة

التصميم المعتمد على الأحداث يُفعَّل عندما يحدث تغيير. تغيّر الحالة هو الحدث. والحدث هو المشغّل.

النموذج 1: الانتقال الذري يحل محل مهمة الإصلاح.

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

# DEFECT: non-atomic transition leaves broken state
def close_pr_on_repo_private(pr_id):
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed'   # الخطوة 1: حالة جزئية
pr.save()                             # مرئي للمستخدمين الآن
# ... قد تفشل الخطوات الأخرى ...
pr.merge_status = 'not_applicable'
pr.save()                             # الخطوة 2: الآن متسق
# مهمة Sidekiq تقوم بالتوفيق إذا فشلت الخطوة 2
# FIX: انتقال ذري؛ لا تظهر حالة وسيطة
def close_pr_on_repo_private(pr_id):
with db.transaction():
pr = PR.get(pr_id)
pr.status = 'branch-forced-closed'
pr.merge_status = 'not_applicable'
pr.save()   # يتم حفظ الحقلين ذريًا؛ لا يُكتبان نصفيًا أبدًا

النموذج 2: التحديث التدريجي يحل محل إعادة الحساب الكاملة.

تقرير يُعاد حسابه من الصفر يُفعَّل لأن البيانات القديمة + البيانات الجديدة = نتيجة جديدة. لكن النتيجة القديمة + التغيير = نفس النتيجة الجديدة، محسوبة تدريجيًا. الحدث: وصول بيانات جديدة. المشغّل: تحديث التجميع للبيانات الجديدة فقط.

# عيب: إعادة حساب كاملة حسب الجدول الزمني
def nightly_totals_job():
for user in all_users():
total = sum(o.amount for o in user.orders)  # scan all time
user.total_purchases = total
user.save()

# FIX: event-driven incremental update
def on_order_placed(order):
order.user.total_purchases += order.amount   # delta only
order.user.save()

يتم تشغيل التحديث التدريجي عند وصول الطلب، وليس في الساعة 2 صباحًا. يحدّث فقط المستخدم المتأثر. يقرأ فقط الطلب الجديد، وليس جميع الطلبات من كل الأوقات. تختفي الوظيفة الليلية.

لماذا يكشف النموذج 1 عن انتقال معطّل

يكشف مقياس القلب المحدد بالنموذج 1 أن انتقال الحالة لم يكتمل. توجد وظيفة الإصلاح لأن مهندسًا لاحظ حالة معطّلة وأضاف آلية مصالحة بدلًا من إصلاح الانتقال. وظيفة الإصلاح: رقعة فوق قرار معماري معطّل.

MOAD-0009 كمضخّم

يضخّم MOAD-0009 المشكلات الأخرى. وظيفة إصلاح الحالة التي تفحص جميع السجلات للعثور على حالة معطّلة: MOAD-0001 (مسح O(N) أو O(N²) في كل تشغيل). وظيفة تقرير تعيد الحساب من الصفر: MOAD-0005 (تدافع ذاكرة التخزين المؤقت عند بدء الوظيفة ووصولها إلى مصدر بيانات دافئ). لا يضر MOAD-0009 بنفسه فقط؛ بل يُسلّم المشكلات الأخرى وفق جدول زمني.

التشخيص وإعادة التصميم

يُشغّل فريق وظيفة cron ليلية في الساعة 2 صباحًا. تفحص الوظيفة جميع الطلبات لكل المستخدمين وتعيد حساب إجمالي مشتريات كل مستخدم من الصفر. تستغرق الوظيفة 4 ساعات. بحلول الساعة 6 صباحًا، تظهر لوحة التحكم الإجماليات الجديدة. بين الساعة 2 صباحًا و6 صباحًا، تظهر لوحة التحكم إجماليات الأمس.

أي شكل من أشكال MOAD-0009 هذا؟ ما الحدث الذي يجب أن يُشغّل إعادة الحساب بدلاً من ذلك؟ ما هي بنية البيانات الوسيطة التي تجعل التحديث تدريجيًا؟