״נוסיף Redis״ היא הצעה לכלי. עדיין חסר התכנון: איזו עבודה מיותרת אנחנו מונעים, מה מותר להחזיר כשהנתון ישן ואיך המערכת מתנהגת בלי שכבת ההאצה. Cache הוא עותק שמוחזק כדי לחסוך עלות, ולכן הוא מכניס שאלות על נכונות לצד שאלות על ביצועים.

נניח שעמוד מוצר מבצע שאילתה מורכבת ואחריה חישוב המלצות. במקום לשמור את כל התשובה ביחד, אפשר להפריד בין פרטי מוצר שמשתנים מעט, מלאי שמשתנה מהר והמלצות שהן ממילא קירוב. ההפרדה מאפשרת לכל נתון מדיניות רענון וכשל משלו.

קודם מודדים את העבודה שרוצים לחסוך

בדקו את התפלגות זמני התגובה, עלות השאילתה, שיעור הבקשות החוזרות ומספר המפתחות הייחודיים. אם כמעט כל בקשה שונה, שיעור הפגיעות ב־Cache עשוי להיות נמוך. אם רוב הזמן נשרף בשירות חיצוני ולא ב־DB, שמירת תוצאת DB בלבד לא תטפל במקור העיכוב.

התחילו מהשערה מדידה: ״קבוצת מוצרים קטנה מקבלת קריאות חוזרות, והחישוב עבורם מהווה חלק משמעותי מזמן התגובה״. עכשיו אפשר לבדוק אם ההשערה נכונה ולחשב אם התועלת מצדיקה זיכרון, תפעול ומורכבות. אין צורך להגיע ל־100% hit rate כדי לקבל ערך, אבל צריך להבין מה מסביר את ההחטאות.

בוחרים יחידת עקביות

כמה זמן מותר שהנתון יהיה ישן? בתמונה או טקסט תיאורי אפשר לעיתים לסבול דקות; בהרשאת גישה או יתרת כסף ההחלטה עשויה להיות שונה לגמרי. אל תבחרו TTL אחד לכל המערכת רק כי הוא נוח להגדרה.

TTL הוא גבול שימושי לתפוגה, אבל הוא לא פרוטוקול עקביות מלא. אם כתיבה ל־DB מצליחה וביטול העותק נכשל, ייתכן שיוחזר מידע ישן עד הפקיעה. אם זה אינו מותר, צריך תכנון חזק יותר: קריאה ממקור האמת, גרסאות, או שינוי בנתון שמותר לשמור.

מבינים את מרוץ ה־Cache Aside

ב־Cache Aside קוראים תחילה מהעותק. בהחטאה קוראים ממקור האמת ושומרים. זהו תהליך פשוט, אך אם עשרות בקשות מגיעות יחד למפתח שפג, כולן עלולות לפנות למסד הנתונים בו־זמנית. התוצאה היא קפיצה בעומס בדיוק כשההגנה נעלמת.

request -> cache hit -> response
        -> cache miss -> database -> cache -> response

אפשר למזג חישובים מקבילים לאותו מפתח, לפזר מעט את זמני התפוגה או לרענן ערכים פופולריים לפני שהם פגים. לכל מנגנון יש גבולות: נעילה מבוזרת מוסיפה תרחישי תפוגה וכשל, ורענון ברקע מחזיק יותר עבודה. בחרו פתרון שמתאים לעלות החישוב ולתדירות הבקשות.

היום שבו ה־Cache נופל

״נחזור ל־DB״ נשמע טוב עד שמבינים שה־DB תוכנן להתמודד עם עשירית מהתנועה. צריך להעריך את עומס ההחטאות בזמן תקלה. לפעמים נכון להגביל בקשות, להשבית זמנית תכונה יקרה או להחזיר עותק ישן שמותר להשתמש בו.

הגדירו timeout קצר מספיק לקריאת Cache, אך אל תבחרו מספר בלי למדוד. retry אגרסיבי של בקשה זולה יכול להעמיס על שכבה שכבר מתקשה. ניטור צריך לזהות לא רק ששרת Redis חי, אלא גם שזמן התגובה ושיעור הפגיעות מאפשרים למוצר לעמוד ביעדים שלו.

מפתחות ונתונים הם חלק מה־API

מפתח צריך לכלול את ממדי הנתון: לקוח, שפה, גרסה או הרשאה לפי הצורך. שכחת מזהה חשבון עלולה להיות גם דליפת מידע, לא רק באג של ביצועים. גרסת schema במפתח יכולה לאפשר מעבר בטוח יותר כשמבנה הערך משתנה.

היזהרו ממנגנון ניקוי שמבוסס רק על התראות. Redis מתעדת Keyspace Notifications כמנגנון מבוסס Pub/Sub; הוא אינו תחליף ליומן אירועים עמיד שצרכן יכול להשלים לאחר ניתוק. קשרו את דרישת האמינות לכלי שבחרתם, במקום להניח שכל הודעה בהכרח תטופל.

תרגיל החלטה

תכננו עמוד שמציג מחיר, מלאי והמלצות. לכל שדה כתבו מקור אמת, זמן התיישנות מותר, TTL והתנהגות בכשל. עכשיו הדמו ירידה חדה ב־hit rate: מה נשבר ראשון, ואיזה אות יגרום לכם לפעול?

בסקירת התכנון הסבירו גם מה בחרתם לא לשמור. לעיתים ההחלטה החשובה ביותר היא להשאיר פעולה רגישה במסלול פשוט ומדויק. אפשר להמשיך עם בחירת אינדקס ב־PostgreSQL, כדי לוודא שה־Cache אינו מסתיר בעיה שקל לתקן במקור.