9.6 - הודעות לא אמינות ופרוטוקול התאוששות - פתרון
פתרון חוזה הניסיון החוזר¶
פתרון ראשון - שינוי מצב אחד¶
המודל הבא מבודד את הלוגיקה העסקית ומאפשר לבדוק כפילות בלי שעון ורשת.
use std::collections::BTreeMap;
#[derive(Default)]
struct Counter {
value: u64,
seen: BTreeMap<u64, (u32, u64)>,
}
impl Counter {
fn apply(&mut self, id: u64, amount: u32) -> Result<u64, &'static str> {
if let Some(&(previous, result)) = self.seen.get(&id) {
return if previous == amount { Ok(result) } else { Err("identity conflict") };
}
let result = self.value.checked_add(amount as u64).ok_or("overflow")?;
self.value = result;
self.seen.insert(id, (amount, result));
Ok(result)
}
}
fn main() {
let mut counter = Counter::default();
for _ in 0..3 { assert_eq!(counter.apply(7, 5), Ok(5)); }
assert_eq!(counter.value, 5);
assert_eq!(counter.apply(7, 6), Err("identity conflict"));
assert_eq!(counter.value, 5);
}
הפונקציה בודקת את הזהות לפני שינוי המצב. אם הבקשה זהה, היא מחזירה את התוצאה המקורית ולא את הערך הנוכחי של המונה, שאולי השתנה בעקבות בקשות אחרות.
פתרון שני - קריסה בין שתי הבטחות¶
הקטע הבא נוסף אחרי הבדיקות במודל ומדגים את הפער במפורש.
השרת שכח שכבר ביצע את הפעולה, ולכן הפעיל אותה שוב. רשומת יומן מתאימה צריכה לכלול זהות לקוח או הפעלה, מזהה בקשה, תוכן פעולה ותוצאה או מידע שמאפשר לשחזר אותה. ההתאוששות צריכה להחיל את שינוי המצב ואת טבלת הזהויות מתוך אותו רצף תקף של רשומות.
אין לשלוח אישור עמיד לפני שהרשומה הגיעה לנקודת העמידות שהחוזה מבטיח. גם מחיקת רשומות ישנות מהיומן צריכה לשמר את חלון מניעת הכפילויות שהובטח. המימוש מפרק האחסון מספק את מנגנון ההתאוששות; כאן אנחנו מגדירים איזה מידע חייב להיכלל בו.
פתרון שלישי - מועד אחד וזהות אחת¶
המועד הכולל נוצר בתחילת הפעולה, וכל ניסיון משתמש באותו מזהה ובאותו תוכן. לפני כל שליחה בודקים שנותר זמן ושלא מוצתה מכסת ארבע השליחות. ההשהיה גדלה לפי המדיניות ונחתכת לפי הזמן שנותר, כך שהניסיון הרביעי אינו מתחיל תקציב חדש.
תשובה מאוחרת נבדקת מול זהות הפעולה ומשך חייה. אם המתקשר כבר קיבל תוצאה לא ידועה, אפשר לעדכן מנגנון בירור נפרד לפי החוזה, אך אסור למסור את התשובה לממתין של פעולה חדשה. כאשר המועד פג, הדיווח הוא שאין תשובה מאומתת בזמן שהוקצב; הוא אינו הוכחה שהשרת לא ביצע את הפעולה.
הודעה ריקה היא הודעה חוקית מבחינת ערוץ ההודעות, אם הפרוטוקול מאפשר אותה. בניגוד לזרם, אין בהודעת אפס בתים סימן מובנה לכך שהעמית סגר כיוון ושלא יישלחו עוד הודעות.