un

guest
1 / ?
back to lessons

نمط الخطوات الأربعة

توجد العيوب في أربعة خطوات سلفية ليست ذرية:

// عيوب
Value value = cache.get(key);
if (value == null) {
    value = expensiveCompute(key);
    cache.put(key, value);
}
return value;

خطوة 1: التحقق من السلة. خطوة 2: الفشل. خطوة 3: الحساب. خطوة 4: الحفظ. جميع الخطوات: ليست ذرية. بين الخطوة 1 وخطوة 4، يمكن لعدد غير محدود من الأخطاء تنفيذ الخطوة 1 وجميعها ترى null.

الخدعة التكرارية

الاستدلال الذي يحمي هذا النمط: "من المسموح أن ينتج عن طريق الخطأ حساب وتنزيل نفس القيمة. النتيجة متكررة. لا يحدث تلوث البيانات."

هذا الاستدلال: صحيح بشأن الصحة. مدمر بشأن التكلفة.

في 1,000 threads عند حدوث فشل في السلة: 1,000 thread كلها تنفذ expensiveCompute(key). إذا كانت expensiveCompute تستدعي قاعدة بيانات، ستطلق 1,000 طلب استعلام قاعدة بيانات في وقت واحد. إذا كانت تُدعى خدمة خارجية، ستطلق 1,000 طلب HTTP في وقت واحد. النظام الذي ينتج النتائج الصحيحة ينهار تحت تكاليف الإنتاج.

ثلاثة محفزات

غزوة ثرثار تنزلق عندما يتم تحويل مفتاح السلة من دافئ إلى بارد في وقت واحد عبر العديد من الأخطاء:

بدء دافئ: إعادة تشغيل الخدمة مع سلة فارغة. موجز الطلب الأول: كل مفتاح يفشل. جميع الحسابات في وقت واحد.

إعادة التشغيل: إعادة التشغيل الدوار يزيل السلة عبر الحالات. توزع الطلبات مجددًا إلى الحالات الباردة.

انتهاء صلاحية TTL: مفتاح مرتفع التردد يتجاوز صلاحية التوقيت. N threads جميعها تتحقق، جميعها تقوم بالحسابات قبل أن يُخزن الناتج الأولي من قبل الخطأ الأول.

جميع المحفزات الثلاث: مرتبطة بارتفاع الطلب. الغزاة ينزلق عندما يكون التردد مرتفعًا والسلة باردة. أفضل وقت ممكن.

مثال Elasticsearch EnrichCache

Elasticsearch EnrichCache: تعليق مكتوب يقرأ "بالطريقة غير مكتفية للتوافق... حسنًا، إذا تم إعادة وضع المفتاح/القيمة بناءً على الأخطاء في حالة سلبية." عند 10,000 مستندًا في الثانية مع سلة تنشيط باردة: جميع 10,000 طلب الاستعلام يلتقاط المخزن المؤقت في نفس الوقت. مخزن المؤقت المصمم للبحث المتكرر يواجه 10,000 استعلامًا متزامنًا. يقلب المجموعة.

الاستدلال التكراري: صحيح في تعليق الكود. كارثي عند 10,000 مستند في الثانية.

ترابط MOAD-0001

MOAD-0001 (Sedimentary Defect) يُنشِئ مشكلة MOAD-0001 (O(N²) إلى O(N)) حُاجزًا في الأنظمة ذات التردد العالي. إصلاح MOAD-0001 (O(N²) إلى O(N)) يفتح هذا العمل المحمول. سرعة التردد يُرسِل طلبات إضافية إلى الأجهزة السفلية. تم حماية المخازن السفلية من قبل حُاجز MOAD-0001، والذي يتلقون الآن موجات ضغط مرئية بالترتيب. MOAD-0005 يتفاعل في المخازن التي لم تُنشِأ من قبل. إصلاح MOAD واحد؛ وضع الآخر.

ما الخطأ الذي يُفترَض أن يُضيع فيه الحبس العددي

تُحوِّل تعليق إيلاستيكسرش (Elasticsearch) على النحو الراقي للتصميم المُحسِن للأسئلة الخاطئة. العدسية: ملكية حقيقية تستحق التفكير فيها. الحبس: إيقاف التحليل عند الصحة دون استمرار في التكلفة.

لماذا يؤدي التعليل "لا يهم إذا كتبت نفس النتيجة اثنين من الأنسجة" إلى الإشكال؟ ما الذي يُحسن في التعليل؟ وما الذي يُغفل؟

computeIfAbsent & singleflight

الإصلاح: جعل التحقق والتنفيذ ذريًا. تُنفذ أنسجة واحدة. تنتظر الأنسجة الأخرى النتائج.

Java: computeIfAbsent

// الإشكال: أربعة خطوات غير ذرية
Value value = cache.get(key);
if (value == null) {
    value = expensiveCompute(key);
    cache.put(key, value);
}
return value;

// الإصلاح: التحقق والتنفيذ الذري
return cache.computeIfAbsent(key, k -> expensiveCompute(k));

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

Go: singleflight.Group

var g singleflight.Group

func getOrCompute(key string) (Value, error) {
    v, err, _ := g.Do(key, func() (interface{}, error) {
        return expensiveCompute(key)
    })
    return v.(Value), err
}

singleflight: إذا كان حسابًا للكائن بالفعل يعمل، ينتظر جميع الدعاوى المتعلقة بالمفتاح ذاته ويشترك في النتيجة الواحدة. حساب واحد، ووصلات نصفية، وناتج مشترك. التمثيل 'flight': التبديل بين الطلبات الموجودة.

Lock vs singleflight

الختم المحدد لكل مفتاح يserializes: يُحسب الجدول 1، ينتظر الجدول 2، ينتظر الجدول 3. بعد انتهاء الجدول 1، يدخل الجدول 2 ويراقب السجل (يتضمن). الجدول 3 يدخل ويراقب السجل (يتضمن). N-1 acquisitions of locks وقراءة السجل.

singleflight يتبديل: الجدول 1 يحسب، الجداول 2 إلى N كلها تنتظر نتيجة الجدول 1. لا acquisitions منفصلات للختم. لا قراءة سجل منفصل. حساب واحد، وناتج، ووزارة N. أقل عمليات من ختم لكل مفتاح.

كلاهما يمنع الطوفان. singleflight يمنع العمل التكراري بشكل أكثر كفاءة.

إعادة كتابة نمط

تطبيق الصيغة على سيناريو محدد.

// مخزن ملفات المستخدم في خدمة Java مرتفعة الطلب
public UserProfile getProfile(String userId) {
    UserProfile profile = profileCache.get(userId);
    if (profile == null) {
        profile = database.loadProfile(userId);  // مكلف: 50ms استعلام قاعدة بيانات
        profileCache.put(userId, profile);
    }
    return profile;
}

إعادة التشغيل الخدمة كل صباح في الساعة 2 ص. في الساعة 8 ص، يطلب 10,000 مستخدم ملفاتهم الشخصية بشكل متزامن.

حدد العيب، اسم وقت إطلاقه، وإعادة كتابة باستخدام computeIfAbsent.