אופטימיזציה של dApp: היכן הממשק מאבד היענות
כאשר המשתמש מאשר עסקה בארנק, הממשק לא אמור להרהר. בפועל הכפתור נשאר לא פעיל מספר שניות, היתרה מתעדכנת באיחור, ורשימת הפעולות האחרונות נטענת רק לאחר רענון ידני. זוהי בדיוק היענות נמוכה של האפליקציה: פורמלית הכול עובד, אך המשתמש לא מבין אם הפעולה שלו נקלטה, ולוחץ שוב.
המקור הראשון לאיבודים — שכבת RPC. כל רכיב פונה לרשת בעצמו: הארנק, לוח היתרה, היסטוריית הפעולות ועלות הגז הנוכחית חיים על טיימרים משלהם ופוגעים באותו צומת.
די לפתוח עשרה טאבים או ליצור מחדש רכיבים במעבר בין מסכים — ואנחנו מקבלים מפולת של בקשות חוזרות לרשת: צומת ציבורי מתחיל להשיב בסירוב, והממשק מציג בשקט נתונים מיושנים במקום שגיאה.
המקור השני — רינדור מחדש של כל עץ הממשק בכל עדכון מצב, גם כשמשתנה ספרה אחת ביתרה; במכשיר חלש זה נראה כקפיצה בגלילה ו"היתקעות" של הקלט.
מדידות ענפיות על צמתים ציבוריים של Ethereum מראות סדרי גודל: ב-dApp טיפוסי עד מחצית מהבקשות מתבררות ככפילות, וההיענות החציונית של הצומת תחת עומס שיא עולה פי כמה לעומת מצב רגוע — כלומר צוואר הבקבוק בעומס אינו בבלוקצ'יין, אלא באופן שבו הממשק מדבר איתו.
אנחנו מפרקים איך זה נסגר: שכבת קריאה אחודה עם מטמון ובריכת חיבורים, מינויים ממוקדים לאירועים במקום סקירה מתמדת, ורינדור מחדש ממוקד רק של אותם צמתים שהנתונים שלהם השתנו.
מה נותנת אופטימיזציה של dApp: היענות, משקל הבנייה, עומס על הרשת
היענות האפליקציה נקבעת לא לפי מהירות הרשת, אלא לפי מה שאנחנו טוענים עד למסך השימושי הראשון. את המסלול הקריטי מקצרים כך: תחילה מציגים נתונים מהמצב המקומי, אחר כך משלימים נתונים מאושרים מהשרשרת.
הזמן עד למסך השימושי הראשון בחיבור סלולרי חלש יורד מכמה שניות לעשיריות — המשתמש מתחיל לעבוד בלי להמתין לאישור הסופי של הפעולה.
בקשות כפולות — הסיבה העיקרית לכך שהאפליקציה נראית כבדה עם גידול במספר הארנקים. דה-דופליקציה לפי מפתח בקשה, איחוד קריאות למנה אחת ומטמון עם זמן חיים מסירים את רוב החזרות.
שיעור הבקשות הכפולות יורד מעשרות אחוזים לאחוזים בודדים, וצמתים חיצוניים מפסיקים להיות צוואר בקבוק גם בכניסה המונית בו-זמנית.
משקל הבנייה בצד הלקוח מקצרים באמצעות פיצול לפי מסלולים והוצאת מודולים כבדים מחוץ למסך הראשון. הבנייה ההתחלתית קטנה פי כמה, והעדכון לא מוריד מחדש את כל הקוד: משתמש חוזר מקבל נתונים חדשים, ולא טעינה חוזרת של כל האפליקציה.
אפקט מדיד נפרד — סילוק מחלקת כשלים בפעילות שיא. תורים לכתיבה, ניסיונות חוזרים עם השהיה גדלה, הגבלת תדירות סקירה ומקור קריאה חלופי מסירים את קיפאון הממשק ברגעים שבהם מספר הפעולות בו-זמניות גדל בחדות. בעבר קפיצות כאלה הובילו לתקיעת מסכים ולעסקאות אבודות, כעת העומס מתחלק באופן צפוי.
אפשרויות האצה: תיקונים ממוקדים, שכבת מטמון, בנייה מחדש של בקשות
שלוש גישות נבדלות לא במורכבות, אלא בסוג צווארי הבקבוק שהן סוגרות. תיקונים ממוקדים מסירים התחממות בקטע מסוים, מטמון ובריכת חיבורים מסירים עומס מהצומת, ובנייה מחדש של שכבת הנתונים משנה את סכימת הפנייה עצמה. בוחרים לפי שלב המוצר ולפי המקום המדויק שבו אובד זמן ההיענות.
| אפשרות | מתי מתאימה | מגבלות |
|---|---|---|
| תיקוני ביצועים ממוקדים | צוואר הבקבוק ממוקם: קריאות RPC חוזרות מיותרות, טעינה כבדה של מסך הפתיחה, משקל בנייה גדל | נותנים תקרה: איבודים מבניים נשארים, עם גידול בעומס האפקט מתמוסס |
| מטמון נתונים ובריכת חיבורים | הרבה קריאות חוזרות ופניות מקבילות לצומת, הקהל גדל | נדרשת מדיניות איפוס מטמון, אחרת המשתמש רואה נתונים מיושנים; כתיבות כך לא מאיצים |
| שליפה באצוות ואינדוקס | עשרות בקשות מאותו סוג למסך, שליפות לפי היסטוריית עסקאות | דורש תיקונים בסכימות ואינדוקס מחדש, תרחישים ישנים יש להריץ מחדש |
| בנייה מחדש של שכבת הנתונים | הלוגיקה אוספת נתונים מכמה מקורות ומבקשת אותם בזה אחר זה | נוגע בחוזים בין מודולים, נדרש ריצה מקבילה ורגרסיה |
בפועל משלבים בין הגישות: תחילה מסירים התחממות ברורה, אחר כך מוסיפים מטמון עם מדיניות איפוס מובנת, ורק לאחר מכן נוגעים בשכבת הנתונים. סדר כזה נותן אפקט מדיד בכל איטרציה ואינו דורש לעצור שחרור תרחישים חדשים.
סדר העבודה: ממדידת צווארי בקבוק ועד מסירת תיעוד
ביקורת ביצועים מתחילה לא בתיקונים, אלא במדידות בסיס: בלעדיהן כל אופטימיזציה הופכת לניחוש. מתעדים את התנהגות האפליקציה בתרחישים אמיתיים — הפעלה קרה, חיבור ארנק, זרם עדכונים מהרשת — כדי להשוות בהמשך את התוצאה לנקודת המוצא, ולא לתחושות.
- מדידת מצב ההתחלה. מסירים מדדים על סטנד שמשחזר את סביבת הייצור: זמן עד מוכנות הממשק, מספר הפניות לצמתי הרשת למסך אחד, נפח הקוד שהלקוח טוען.
- פרופיילינג של האפליקציה. עבור חלק הלקוח אנו משתמשים בפרופיילר של הדפדפן, עבור חלק השרת — בפרופיילר של סביבת ההרצה. נראה היכן באמת מתבזבז הזמן: קריאות חוזרות של אותו מצב, רינדורים מחדש מיותרים, פעולות סינכרוניות בתהליכון הראשי.
- סכימת שינויים. מתאמים תוכנית לפני קוד: אילו בקשות מאחדים למנה, מה מכניסים למטמון ולכמה זמן, אילו נתונים נלקחים מהאינדקס במקום קריאות ישירות מהרשת.
- הגדרה. מיישמים לפי הסכימה: מפצלים את קוד הלקוח לחלקים עם טעינה לפי דרישה, מוסיפים ניסיונות חוזרים עם השהיה גדלה עבור צמתים לא יציבים, מגבילים את תדירות הבקשות.
- בדיקה על סטנד. מריצים תרחישים תחת עומס, משווים למדידת הבסיס, ובודקים בנפרד את הדרדור כאשר הצומת אינו זמין. לא הגענו למספרי היעד — חוזרים לפרופיילינג.
- מסירה לצוות. מוסרים תיעוד, קונפיגורציות, לוח ניטור ונוהל החזרה לאחור, כדי שהתחזוקה לא תהיה תלויה בנו.
כל שלב נסגר בארטיפקט — דוח, סכימה או קונפיג — ולכן מצב העבודה נראה בכל שלב.
קונפיגורציות, סכימות ותקנות שנשארים אצל הצוות
העבודה מסתיימת לא בדוח, אלא בסט ארטיפקטים שהצוות משתמש בהם בהמשך בלעדינו. כל אחד עונה על שאלה משלו: איך מוגדרים הצמתים, לאן הולכות הבקשות, למי יש גישה, מתי מתאפס המטמון ואיך מתעדכנות התלויות.
- קבצי קונפיגורציה של נקודות צומת: מגבלות בקשות בו-זמניות, פסקי זמן, ניסיונות חוזרים בעת ניתוק, גודל בריכת חיבורים — עם ציון לאיזה פרופיל עומס הוגדר כל ערך.
- מפת זרימות נתונים: מה יוצא לשרשרת, מה נסגר במטמון, היכן עובר הגבול ביניהם ואיך הוא זז עם גידול הקהל.
- מדיניות גישה: תפקידים והפרדת הרשאות קריאה וכתיבה, סדר סבב מפתחות שירות, מגבלות על הצוות ועל אינטגרציות חיצוניות.
- כללי ביטול תוקף מטמון: זמני חיים, הצמדה לבלוק חדש ולאירועי חוזה, חימום מפתחות חמים לאחר עדכון, התנהגות בעת אי-סנכרון.
- ספי גודל לבנייה בצד הלקוח: מה נטען ראשון, מה נטען לפי דרישה, עץ תלויות והנקודה שאחריה הבנייה נחשבת לכבדה יותר.
- נוהל עדכון תלויות: תדירות, בדיקת תאימות במעגל בדיקות, חלון לחזרה לסט הגרסאות הקודם.
- סכימת ניטור ותורנות: מדדי השהיה, שיעורי שגיאות ופגיעות במטמון, ספי התראות, סדר פעולות בעת דרדור.
מקרה בוחן: איך הסרנו את ירידת ההיענות בעומס שיא
מה היה
אפליקציה ברשת Polygon החזיקה צומת גישה יחיד לרשת, וכל מסך משך מצב באופן עצמאי. בשיא — מכירת קולקציה, כמה אלפי סשנים בו-זמניים — אותם נתונים התבקשו 3–4 פעמים ברצף: שיעור הבקשות הכפולות הגיע ל-38%.
זמן התגובה של הממשק באחוזון ה-95 עלה מ-1.2 ל-8 שניות, חלק מהקריאות נפלו בגלל פסק זמן — המשתמש ראה יתרה ריקה, לחץ שוב על הכפתור והוסיף עומס.
מה שינו
הוצאנו את הגישה לרשת לשכבה נפרדת: בריכת ספקים עם מעבר בעת דרדור, כיווץ קריאות זהות בתוך מחזור רינדור אחד ואיחוד קריאות קטנות לבקשה משותפת. המצב החל להישמר במטמון לפי מספר בלוק, המינויים לאירועים — להתרכז בחיבור אחד במקום זרם לכל רכיב. מהבנייה הסרנו ספריות כבדות, והשארנו רק חלקים בשימוש.
מה נהיה
כפילות הבקשות ירדה מ-38% ל-4%, זמן התגובה של הממשק באותו אחוזון — ל-1.2 שניות, והוא לא עולה בזרימה גדולה פי עשרה. משקל הבנייה הצטמצם פי שלושה, המסך הראשון הפסיק להמתין לטעינת קוד שאינו בשימוש.
מחלקת הכשלים "חריגה מזמן ההמתנה בקריאת מצב" נעלמה לחלוטין: במהלך ריצת מבחן עומס — אף לא אחד.
במה שונה מטמון בצד הלקוח ממטמון בצד השירות?
מטמון בצד הלקוח חי בזיכרון האפליקציה או באחסון מקומי ומסיר פניות חוזרות לצומת בחזרה למסך פתוח. מטמון בצד השרת עומד לפני צמתי הרשת ומחזיר תשובה מוכנה אחת לכל מי שמבקש את אותו הדבר — כך מפסיקים להיתקל במגבלות פניות. אלו רמות שונות עם מחיר שגיאה שונה, ואין להחליף אחת באחרת.
| רמה | מה מאיץ | היכן יוצר אי-סנכרון | מה מבטל את התוקף |
|---|---|---|---|
| צד לקוח | רינדור מחדש, חזרה למסך, קריאת יתרה | החלפת חשבון, החזרת בלוק לאחור, החלפת רשת | אירועי חוזה, החלפת רשת וכתובת, זמן חיים קצר |
| צד שרת | קריאות חוזרות מאותו סוג ממשתמשים רבים | פיגור מגובה הבלוק, מרוץ בכתיבה | גובה בלוק, גרסת מפתח בקשה, זמן חיים |
אי-סנכרון נתונים מופיע במקום שבו המטמון שורד שינוי במצב הרשת. העסקה אושרה, אבל האפליקציה מציגה יתרה ישנה — והבקשה הבאה יוצאת עם נתוני קלט שגויים. מקרה נפוץ שני — החלפת חשבון ב-MetaMask: התשובה לפי הכתובת הקודמת נשארת תקפה לפי המפתח, אבל שגויה במשמעות.
משלבים את הרמות כך: הלקוח אחראי למהירות הרינדור, השרת — לחיסכון בפניות, והאמת תמיד מגיעה מהרשת. ביטול תוקף מטמון נשען על אירועים וגובה בלוק: אישור עסקה מאפס מפתחות קשורים, ושכבת השרת שומרת את התשובה יחד עם מספר הבלוק ולא מחזירה אותה אם הוא מפגר. שכבת הלקוח מקבלת זמן חיים קצר ואיפוס כפוי בעת החלפת חשבון או רשת.
ספקות טכניים: מה מבררים לפני תחילת העבודה
ספקות לפני התחלה הם בדרך כלל אותם ספקות: המוצר עובד, מפחיד לשבור אותו, והאפקט מהאופטימיזציה לא ברור. להלן חמש שאלות שנשאלות לרוב, ואיך אנחנו עונים עליהן.
המוצר כבר עובד — האופטימיזציה לא תשבור אותו?
תחילה מדידות, אחר כך תיקונים: עושים פרופיילינג לתרחישים, מוצאים את הקריאות היקרות ביותר ומשנים אותן ממוקד, בלי לכתוב מחדש חלקים עובדים. הפריסה מתבצעת במנות תעבורה, הבנייה הקודמת נשארת זמינה להחזרה לאחור, ולכן שינוי כושל מוחזר תוך דקות, לא תוך לילה.
ומה אם צוואר הבקבוק בכלל לא באפליקציה?
בודקים את כל השרשרת: זמן התגובה של צמתי הרשת, מגבלות נקודת הגישה, עלות ותור עסקאות. לעתים קרובות האפליקציה לא אשמה — היא נתקלת בצומת עמוס או במגבלות ספק, ואז הפתרון הוא החלפת גישה או צומת עצמאי עם גיבוי.
האם מורכבות התחזוקה לא תגדל אחרי השינויים?
מטמון, תורים ומשימות רקע מוסיפים רכיבים שצריך לתחזק. כדי שזה לא יהפוך לקופסה שחורה, מתעדים את סכימת האינטראקציה, כללי התיישנות הנתונים וסט מדדים שלפיהם רואים תקלה לפני שהמשתמש מדווח עליה.
מה עושים עם חוזים שכבר הופצו ותאימות?
בחוזים שהופצו נוגעים רק בעת צורך מפורש ועם תוכנית מיגרציה נפרדת. בודקים התנהגות בארנקים נפוצים, טיפול בחתימות חוזרות ופערים במצב — התקלות העיקריות אחרי אופטימיזציה מתרחשות בדיוק בממשק הזה.
מה קורה אחרי מסירת העבודה?
מוסרים תיעוד, סכימות וקונפיגורציות, ועוברים עם הצוות שלכם על נקודות בקרה ותקלות אופייניות. בהמשך אפשרי פרק תמיכה מוסכם: תיקונים, ניטור וחזרה למצב הקודם בכל שלב, עד שהשינויים יראו את עצמם בעומס אמיתי.
נדון במשימה: מה נדרש מכם להערכה
כדי שההערכה לאופטימיזציה של dApp תהיה ממוקדת ולא "פחות או יותר", אנחנו צריכים מינימום נתוני פתיחה. איתם נחשב במעבר אחד תקציב לשינוי נתיבים חמים ונחזור עם תוכנית: מה משנים בקונפיגורציית הצמתים, היכן שמים מטמון, ואילו קריאות חוזה מוציאים מהנתיב התדיר.
- תרחיש משתמש: מה בדיוק עושה האדם — כניסה לארנק, החלפה, מינט, סטייקינג, היסטוריית פעולות — ובאיזה שלב כרגע מורגשת ירידה.
- נקודות מדידה: אילו מדדים נאספים היום — זמן תגובת הצומת, שיעור קריאות שנדחו, שיעור פגיעות במטמון, נפח הנתונים המועברים — ובאמצעות מה הם נאספים.
- נקודות כשל: היכן בדיוק התרחיש נשבר תחת עומס — בקריאת מצב, בשליחת חתימה, בהמתנה לאישור.
- מגבלות זמנים: חלון עצירת שירות מותר, שעות עומס שיא שאסור לפגוע בהן, זמינות סטנד לבדיקות.
- גישה לסטנד: עותק בדיקה של הרשת והחוזים, מפתחות בדיקה, מגבלות מכסת בקשות אצל ספק הצמתים.
- סטאק וקונפיגורציה נוכחית: אילו שכבות מטמון ואינדקסרים כבר מותקנים, האם יש צומת עצמאי או שעובדים דרך נקודות גישה חיצוניות.
- תוצאה צפויה: מה נחשב להצלחה — זמן תגובה יעד, שיעור פגיעות במטמון, נפח הנתונים המועברים ללקוח.
עם סט כזה אנחנו תחילה משחזרים את התרחיש שלכם על סטנד ומתעדים מדידות בסיס, ורק אחר כך מציעים שינויים — אחרת האופטימיזציה הופכת לניסוי וטעייה של השערות. תארו את המשימה — נחזור עם הערכת עבודה, תוכנית לפי שלבים וקריטריוני קבלה.







