מערכת מלאי: מה נשבר בפעולות מקבילות
מערכת מלאי קלאסית פועלת לפי סכמת "קרא — שנה — כתוב": בקשה לוקחת את מצב הפריט הנוכחי, משנה אותו בזיכרון ושומרת בחזרה. כל עוד הפניות מגיעות אחת-אחת, זה בלתי מורגש. בהחלפות או יצירה במקביל, שתי הבקשות קוראות את אותה גרסת נתונים, והשנייה דורסת בשקט את התוצאה של הראשונה.
ביצירה משותפת הדבר מייצר יתרות שליליות: שני לקוחות מורידים את המופע האחרון של המשאב, והשרת מאשר את שתי הפעולות. החלפות מייצרות אפקט מראה — פריט אחד עובר לשני מקבלים, כאשר החיוב וההענקה אינם מבוצעים כטרנזקציות פריט אטומיות. בדיקת הבעלות עוברת בהצלחה אצל שניהם, כי בין הבדיקה לכתיבה המצב עוד לא השתנה.
כפילויות פריטים מגיעות גם מבקשות חוזרות. השחקן לוחץ על רכישה, התשובה לא מגיעה בגלל ניתוק תקשורת, הלקוח חוזר על הבקשה, והשרת מעבד אותה כחדשה: בלי מזהה פעולה הזיכוי עובר פעמיים, ובמלאי מופיע מופע שני.
אובדן רכישות בעת ניתוק תקשורת — הצד השני של אותה מכניקה: התשלום אושר על ידי הפלטפורמה, אך הרשומה במלאי בוטלה או לא הגיעה. הלקוח מציג מצב ישן מהמטמון המקומי, חוסר הסנכרון בין הלקוח לשרת מצטבר ומתפצל בכל כניסה, עד שמצב השרת נעשה בלתי צפוי עבור השחקן.
מכאן מתחיל טיפול ידני: זרם הפניות לתמיכה גדל, תיקונים מבוצעים ישירות במסד החי בלי תיעוד מי ומתי שינה את הרשומה. נבחן כיצד סוגרים זאת.
מה נותנת אטומיות הפעולות: שלמות, מהירות, דחיות
אטומיות — זה כאשר החלפה, יצירה או העברת פריטים נרשמות במלואן או לא נרשמות כלל. מצב שבו פריט נעלם מאצל שחקן אחד ולא הופיע אצל אחר אינו נותר אפילו בעת ניתוק תקשורת ברגע הכתיבה. עבור הפרויקט זה ההבדל בין פנייה בודדת לתמיכה לגל תלונות לאחר אירוע המוני.
שלמות בעת החלפות, יצירה והעברת פריטים המוניות
כל פעולה עוברת בדיקת שרת ונכנסת ליומן השינויים כרשומה אחת: החיוב והזיכוי מתבצעים בתוך טרנזקציה משותפת עם נעילה על התאים המושפעים. יצירה הצורכת כמה ערמות, והחלפה בין שחקנים מצייתות לאותו כלל, ולכן "חצי" ממתכון לא יכול להתבצע.
אידמפוטנטיות הבקשות סוגרת את השליחה החוזרת: אם השחקן לא המתין לתשובה ולחץ שוב, החיוב השני לא יתבצע — החזרה מזוהה לפי מפתח הפעולה, ובמלאי נותר בדיוק שינוי אחד.
תגובה בעומס שיא והסרת מחלקת הדחיות
בעומס שיא — פתיחת עונה, אירוע, חלוקת פרסים — צוואר הבקבוק אינו הלוגיקה עצמה אלא התחרות על אותם תאים. אנו מפזרים נעילות על קבוצת האובייקטים המינימלית ומחזיקים פעולות תכופות בזיכרון, ואת הכבדות — בתור עם עיבוד סדרתי. זמן התגובה לפעולות רגילות במלאי אינו גדל יחד עם מספר השחקנים.
מחלקת הדחיות משתנה באופן מהותי: במקום "הפריט נעלם" או "הפריט הוכפל" נותרות רק דחיות מסוג "הפעולה נדחתה, המצב לא השתנה". מקרים כאלה השחקן רואה כהודעה מובנת ויכול לחזור על הפעולה, ולא כאובדן התקדמות.
בקבלה זה נבדק בפשטות: אי-התאמות לאחר החלפות המוניות, שיעור הכפילויות וזמן התגובה בשיא — שלושה מדדים שלפיהם רואים שמערכת המלאי עומדת בעומס של משחק חי.
כיצד בנויה מערכת המלאי: מצבי אחסון וסנכרון
הבחירה בין יומן אירועים למצב נוכחי
יומן אירועים שומר כל פעולה של השחקן כרשומה נפרדת ומאפשר לשחזר את המלאי בכל נקודת זמן. המצב הנוכחי מחזיק תמונת מצב מוכנה — נקרא בזול יותר, אך היסטוריית השינויים אובדת אם לא לכתוב אותה בנפרד.
לרוב נוקטים בהיברידי: תמונת מצב כמקור להעברה ללקוח בתוספת יומן הפעולות האחרונות לניתוח מצבים שנויים במחלוקת. ניהול גרסאות של הרשומה — מספר גרסה לכל שינוי — מאפשר חזרה למצב הקודם ומגן מפני החלה חוזרת של אותה פעולה.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| יומן אירועים | נדרשת היסטוריה מלאה, ניתוח תלונות, חזרה לאחור | הנפח גדל, הקריאה דורשת הרכבה מחדש |
| מצב נוכחי | קריאה תכופה, מלאי פשוט | אין היסטוריה, חזרה לאחור רק מגיבוי |
| תמונת מצב + יומן | איזון בין מהירות קריאה ליכולת ניתוח | סכמה מורכבת יותר והעברת נתונים |
היכן עובר הגבול בין הלקוח לשרת
הלקוח מצייר את המלאי ומציג את התוצאה מיד, אך אינו מקור האמת: כל שינוי מאושר על ידי השרת. השרת מאמת את הפעולה, מציב נעילה על פעולות השחקן למשך הטרנזקציה, ורק לאחר מכן רושם את השינוי — כך שתי בקשות מקבילות לא יחייבו פריט אחד פעמיים. אם האישור לא הגיע, הממשק מחזיר את התוצאה המוצגת וקורא מחדש את המצב מהשרת.
שלבי ההטמעה: מביקורת פעולות ועד מסירת נהלים
הטמעת מערכת המלאי מתחילה לא בקוד, אלא בניתוח כיצד פריטים מגיעים כעת לשחקן ומה קורה להם בהמשך. אנו מתארים כל פעולה — הענקה כפרס, רכישה, החלפה, הוצאה, החזרה בעת ביטול — ומסמנים מקומות שבהם המאזן יכול להתפצל: ניתוק תקשורת, בקשה חוזרת, פעולות מקבילות מאותו חשבון.
ביקורת הפעולות הנוכחיות ומודל הנתונים
- אוספים את רשימת הפעולות והכללים: מגבלות על פריטים, תקופות חיים, שיוך לאירוע או לעונה, מי יוזם את השינוי — הלקוח, שרת המשחק או שירות חיצוני.
- מנתחים את מודל הנתונים הנוכחי: היכן נשמר המצב, כיצד קשורים פריטים וערמות, האם קיים יומן שינויים והאם ניתן לשחזר לפיו את המלאי בכל נקודת זמן.
- מתכננים את הסכמה היעד: יומן פעולות עם מפתח בקשה ייחודי, כדי ששליחה חוזרת לא תעניק פריט פעמיים, וכללי נעילה ברכישות מקבילות.
בדיקה בתקלות ובבקשות חוזרות
- מריצים תרחישי כשל: ניתוק תקשורת ברגע החיוב, כפילות בקשה, רכישה מקבילה של הפריט האחרון, קריסת השירות בין החיוב להענקה — המצב חייב להתכנס.
- מעבירים את המרשם לסכמה החדשה במצב צל: החישוב הישן והחדש פועלים במקביל, אי-ההתאמות נבדקות שורה-שורה, וחזרה לאחור זמינה עד לרגע המעבר.
- מוסרים את נהלי התפעול: מדדי אי-התאמות, התראות על אנומליות, סדר תיקונים ידניים עם רישום חובה ביומן הביקורת וחזרה מובנת לגרסה הקודמת.
ערכת מסירה: סכמות נתונים, מדיניות גישה, נהלים
את ערכת המסירה אוספים כך שצוות המזמין יפתח את מערכת המלאי בלעדינו: מהנדס חדש מבין ממנה את מבנה הפריטים בתוך יום, והתורן — מה לעשות בעת אי-התאמת מאזנים. כל המתואר נבדק על קו עבודה פעיל, ולא שוחזר מהזיכרון ביציאה מהפרויקט.
- סכמת נתוני הפריטים: ישויות, קשרים, כללי ערימה, מזהי מופעים ואיסורים שאסור להפר.
- מדיניות גישה ותפקידי מנהלים: מטריצת הרשאות להענקה, משיכה ותיקון מאזנים.
- מערך בדיקות שלמות: חיוב כפול, הענקה מקבילה, חזרה לאחור של טרנזקציה שלא הושלמה.
- תיעוד שחזור והשוואה: נקודות שחזור, סדר פעולות בעת תקלה, סימני התכנסות.
- נהלי תפעול: רוטציית מפתחות, הענקת הרשאות, הסלמת אירועים.
- יומן שינויי סכמה: גרסאות, מיגרציות, תאימות למלאים שנשמרו קודם.
מדיניות גישה ותפקידי מנהלים
את ההרשאות מפרידים לפי מינימום נדרש: מנהל רגיל מעניק פריטים לפי בקשה, הבכיר מתקן מאזנים, ושינוי סכמה זמין לחוג מצומצם. כל פעולה נכנסת ליומן בלתי ניתן לשינוי עם המבצע — הוויכוח "מי העניק" נסגר בתוך דקה.
תיעוד שחזור והשוואה
מתארים שני תרחישים: חזרה למצב שלם והשוואה לאחר תקלה חלקית. מצוינים סדר הפעולות, האחראים והסימנים שלפיהם התורן מבין שהמלאי מתכנס שוב.
מקרה בוחן: הסרנו כפילויות פריטים ואובדן רכישות בעומס
הפרויקט — משחק מובייל עם כלכלה בתוך המשחק: פריטים, ערכות, רכישות במטבע פנימי ובתשלומים. אלינו הגיעו לאחר סדרת אירועים בעונתיים, כאשר מספר השחקנים שגדל חשף את נקודת התורפה של מערכת המלאי.
מה היה לפני: השוואה ידנית וזרם פניות
חשבון הפריטים התנהל בשני מקומות: חלק מהמצב נשמר בפרופיל השחקן, חלק — בתור הפעולות. בעת ניתוק תקשורת בין החיוב לרישום, השחקן יכול היה לקבל פריט פעמיים או לאבד רכישה ששולמה.
התמיכה אספה מדי שבוע תלונות, והמהנדסים השוו ידנית את המרשם ליומן הפעולות — ההשוואה ארכה יום עבודה, ועד מחצית מאי-ההתאמות נמצאו רק דרך הפניות. בדקות הראשונות של אירוע מספר התלונות הללו גדל בכפולות.
מה נוצר לאחר: מדדים וסיכונים שהוסרו
הענקת הפריטים הועברה לטרנזקציות אטומיות עם מפתח פעולה: בקשה חוזרת לא יוצרת רשומה שנייה אלא מחזירה את התוצאה של הראשונה. הפרויקט פועל על Unity, אך את חלק השרת של המלאי אנו מחזיקים בלתי תלוי בלקוח — עדכון המשחק לא שובר את החשבון.
לפי תוצאות תקופת הביקורת: כפילויות פריטים במרשם — אפס, פניות על רכישות שאבדו הצטמצמו בכשש פעמים, ההשוואה האוטומטית של המרשם אורכת דקות במקום יום עבודה. שיא העומס עבר בלי אי-התאמות בין המרשם ליומן הפעולות.
הסיכונים שהוסרו — הענקה כפולה בבקשות חוזרות, אובדן רכישה ששולמה בעת ניתוק תקשורת, אי-התאמת מאזנים בין שירותים.
מקורות מאמתים — לוחות ניטור, יומן טרנזקציות והפקות של פניות תמיכה לתקופות דומות לפני ואחרי.
במה שונה טרנזקציה אטומית מנעילת רשומה?
נעילת רשומה מפרידה פעולות בזמן: הזרם הראשון תופס את הרשומה, והשאר ממתינים לשחרורה. טרנזקציה אטומית אחראית לדבר אחר — מערכת השינויים מוחלת במלואה או מבוטלת, בלי להשאיר חצי מצב. לכן הבחירה אינה ביניהן, אלא אילו נעילות חיות בתוך הטרנזקציה וכמה זמן.
בעת קונפליקט בין פעולות, הגישה הפסימית מכריחה את המשתתף השני להמתין, ובפעולות המוניות התגובה גדלה יחד עם אורך התור — עד כדי דחיות בזמן. הגישה האופטימית אינה ממתינה: היא קוראת את המצב עם גרסה, בעת הכתיבה משווה אותה, ואם הנתונים כבר השתנו — חוזרת על הפעולה מחדש. המסלול המהיר — מעבר אחד, היקר — חזרה.
הבחירה תלויה בתדירות הקונפליקטים על שורה אחת. כל עוד על פריט אחד מתחרים לעיתים רחוקות — פרסים מגיעים לתאים שונים — הגישה האופטימית נותנת אפס המתנות וכמעט לא מבזבזת משאב על חזרות.
אך אם כמה זרמים כותבים כל העת לאותה ערמה, החזרות מגיעות במפולת, והגישה הפסימית עם החזקה קצרה של הנעילה צפויה יותר.
עבור מלאי חשוב שהחלפה, הרכבת פריט ממשאבים והעברה בין שחקנים יתבצעו בטרנזקציה אטומית אחת: החלפה שמוחלת חלקית גרועה מדחייה.
אנו מגבילים את מספר החזרות, מנהלים מפתח אידמפוטנטיות מפני כפילויות בעת שליחה חוזרת של בקשה ועוקבים אחר שיעור הקונפליקטים — הוא מרמז מתי הגיע הזמן להחליף את הסכמה.
מגבלות ומלכודות: שאלות טכניות נפוצות
לפני ההתחלה, הספקות הטכניים נוגעים לא ללוגיקת הפריטים, אלא לאופן שבו השכבה החדשה מסתדרת עם שרת פעיל. להלן מה שצף לרוב בשלב ההערכה: הפעלה בפרויקט חי, כפילויות שנצברו, ניתוקי תקשורת, מיגרציה והרשאות גישה.
האם אפשר להטמיע בלי לעצור את המשחק?
כן, עצירה אינה נדרשת. השכבה החדשה מוצאת כשירות נפרד עם סכמת הנתונים שלה, והלוגיקה הישנה ממשיכה לשרת את המלאי. ההפעלה מתבצעת מאחורי מתג, ותחילה על חלק מהשחקנים — אם הבדיקות סוטות מהמצופה, המתג מוחזר לאחור.
מה לעשות עם כפילויות שכבר נצברו?
אותן מפרקים לפני ההפעלה. ההשוואה מתבצעת לפי יומן הפעולות: עבור כל עמדה משחזרים את שרשרת הזיכויים ומזהים יתרה נכונה אחת. את העודף מוחקים בפעולה נפרדת עם רישום בביקורת, כדי שאפשר יהיה להסביר את ההיסטוריה לשחקן ולתמיכה.
מה יקרה בעת ניתוק תקשורת או שליחה חוזרת של בקשה?
כל פעולה נושאת מזהה ייחודי: בקשה חוזרת לא יוצרת פריט שני — השרת מזהה אותה ומחזיר את התוצאה של הראשונה. הטרנזקציה מאושרת במלואה או מבוטלת, ולכן מצב של "חויב אך לא זוכה" אינו נוצר.
על מי מוטלת המיגרציה וכיצד מופרדות ההרשאות?
את המיגרציה מובילים אנו: מעבירים יתרות, בודקים התכנסות לפני ואחרי, ומחזיקים מסלול חזרה לאחור. מהמזמין נדרשים גישה לאחסון ואדם שמאשר את הכללים לגבי פריטים שנויים במחלוקת.
את ההרשאות מחלקים לפי תפקידים: התמיכה רואה את ההיסטוריה אך אינה משנה יתרות; זיכויים במסגרת המגבלה — אצל הכלכלנים; ספר הפריטים — אצל המנהלים.
נדון במשימה: מה לשלוח להערכה ולתוכנית עבודה
כדי לחזור עם הערכת עבודות, ולא עם טווח "מכאן ועד כאן", נדרשים נתוני מקור על מערכת המלאי: כללי הענקה וחיוב של פריטים, מגבלות על פעולות מתחרות, פלטפורמות יעד. ככל שהנתונים הראשוניים קונקרטיים יותר, כך מדויקים יותר תוכנית ההטמעה והרכב הצוות.
- כללי מלאי: קטגוריות פריטים, ערמות, מגבלות תאים, שיוך לדמות או לחשבון.
- סכמת נתונים ואחסון נוכחי: מבנה הטבלאות, היכן נשמר המצב, האם קיים יומן פעולות.
- דרישות לאטומיות: אילו פעולות חייבות לעבור במלואן — מכירה, החלפה, יצירת פריט, פרס על קרב — ומה קורה בעת ניתוק תקשורת.
- תרחישי עומס: מספר שיא של שחקנים במקביל, תדירות פעולות, התפרצויות עונתיות.
- פלטפורמות יעד וענפי לקוח: היכן האפליקציה כבר הופצה, אילו מגבלות יש לחנויות.
- גישות: מאגר קוד, סביבת בדיקות, הפקה של נתונים אנונימיים לבדיקת השערות.
- מגבלות הפרויקט: לוחות זמנים, שלביות ההפצה, מי בצד המזמין מקבל החלטות לגבי כללי המשחק.
לאחר מכן אנו מכנסים זאת לסדר עבודה: תחילה קווי המתאר של הנתונים והמודל הטרנזקציוני, אחר כך הלוגיקה בצד השרת, ואחר כך החלק בצד הלקוח והבדיקות בתרחישים מתחרים. בתוצאה — הערכת עבודות, תוכנית הטמעה בשלבים והרכב צוות בהתאם להיקף שלכם. תארו את המשימה — נחזור עם הערכה ותוכנית.







