הרגע שהבנו שיש בעיה
היה יום שלישי בבוקר, אחד הלקוחות שלנו שלח הודעה פשוטה: "הדשבורד שלכם איטי מאוד הבוקר." ענינו לו בנימוס, בדקנו מהצד שלנו — ונראה היה תקין. אבל משהו נשאר לי בבטן. פתחתי את הדשבורד בעצמי, הפעם עם עיניים של משתמש ולא של מפתח. וראיתי את זה: כל טעינה של רשימת השיחות לקחה כמעט שלוש שניות. לחיצה על שיחה — שנייה וחצי. רענון — שוב שלוש שניות.
אמרתי לעצמי: זה לא קביל. אנחנו בונים פלטפורמת WhatsApp שאמורה לעזור לעסקים לענות מהר — ואנחנו בעצמנו איטיים.
אז החלטנו לעצור הכל ולעשות אודיט ביצועים רציני. לא תיקון נקודתי, לא "בוא נראה מה קורה" — אודיט מלא, מקצה לקצה. מה שמצאנו הפתיע אותנו.
האודיט: מצאנו 28 בעיות
חילקנו את הבדיקה לשלוש שכבות: ה-backend, שכבת ה-API, וה-frontend. בכל שכבה רשמנו כל בעיה שמצאנו — גדולה, קטנה, בינונית. בסוף היו לנו 28 פריטים ברשימה. חלקם היו מביכים. לא כי היו מסובכים — אלא כי היו כל כך בסיסיים.
הבעיה הכי מביכה: I/O חוסם בתוך asyncio
הפלטפורמה שלנו בנויה על Python עם asyncio — ארכיטקטורה אסינכרונית שאמורה לאפשר טיפול בהרבה בקשות במקביל. אבל מה שגילינו היה שבכמה מקומות קריטיים, קראנו לפעולות I/O חוסמות ישירות בתוך ה-event loop. זה כמו לשים פקק בכביש מהיר — כל הבקשות עצרו לחכות.
הפתרון היה לעטוף את הפעולות האלה ב-asyncio.to_thread, שמוציא את הפעולה החוסמת לתהליכון נפרד ומשחרר את ה-event loop להמשיך לעבוד. זה לא פתרון מגניב או חדשני — זה סטנדרט. אבל פשוט לא עשינו את זה. ותיקון הנושא הזה לבד הוריד זמן תגובה בצורה משמעותית.
בעיית N+1: כשהמסד נתונים עובד קשה מדי
זו בעיה קלאסית שכל מפתח מכיר — ובכל זאת נפלנו בה. כשטענו רשימת שיחות, ירינו שאילתה אחת לקבלת השיחות, ואז שאילתה נוספת לכל שיחה כדי לקבל נתונים נוספים. עם 50 שיחות — 51 שאילתות. עם 200 שיחות — 201 שאילתות.
החלפנו את הגישה הזו בשאילתות מצטברות עם GROUP BY שמביאות את כל הנתונים הדרושים בפנייה אחת לבסיס הנתונים. התוצאה הייתה דרמטית — זמן השאילתות ירד בעשרות אחוזים.
שכבת ה-frontend: React Query שלח יותר מדי בקשות
ה-frontend שלנו בנוי עם React, ואנחנו משתמשים ב-React Query לניהול הנתונים. זה כלי מעולה — אבל גילינו שלא הגדרנו אותו נכון. כשמשתמש פתח מספר טאבים או ניווט בין דפים, React Query ירה בקשות כפולות לאותם endpoints. שישים אחוז מהבקשות היו מיותרות לחלוטין.
הגדרנו כללי deduplication נכונים — שReact Query ידע שאם כבר נשלחה בקשה לנתון מסוים ותוצאה עדיין תקפה, אל תשלח שוב. קיצרנו את מספר הבקשות ב-60% בלי לשנות שורה אחת של לוגיקה עסקית.
הנגיעה החכמה ביותר: delta polling
אחת הבעיות שגילינו היא שה-frontend שלנו ביקש עדכונים באופן קבוע — וכל פעם קיבל את כל רשימת ההודעות מחדש. גם אם לא היה שינוי. גם אם הגיעה הודעה אחת חדשה — הורדנו את כל הרשימה.
בנינו endpoints חדשים שתומכים ב-delta polling: הלקוח שולח timestamp של הבדיקה האחרונה, והשרת מחזיר רק מה שהשתנה מאז. פחות נתונים, פחות עיבוד, פחות זמן המתנה. הדשבורד מרגיש עכשיו בזמן אמת.
הרגע שהכי הפתיע אותנו
אחרי שתיקנו את 20 הבעיות הראשונות, חשבנו שסיימנו. מדדנו את הביצועים — ושמחנו. הזמנים ירדו יפה. אבל אחד מהמפתחים אמר: "בוא נמשיך לעוד שבוע ונראה מה עוד יש." ומצאנו עוד 8 בעיות. חלקן קטנות — כמה מילי-שניות כאן ושם. אבל ביחד הן עשו את ההבדל בין "מהיר" ל"מרגיש מיידי".
הלקח: אל תעצרו כשנראה שמספיק. המשיכו לחפור.
מה עשינו שיטתית: 28 פריטים, אחד אחד
הנה הגישה שעבדה בשבילנו:
- תיעדנו כל בעיה לפני שתיקנו אותה — כולל מדידה של הזמן לפני ואחרי
- טיפלנו בבעיות לפי השפעה — קודם מה שיביא את האפקט הגדול ביותר
- בדקנו כל תיקון בנפרד — לא שינינו שני דברים ביחד, כדי שנדע בדיוק מה עזר
- הרצנו בדיקות עומס אחרי כל סבב של תיקונים
- שמרנו על רשימה חיה — כל מה שמצאנו נכנס לרשימה, גם אם לא תיקנו מיד
התוצאות: מה השתנה בפועל
אחרי שתיקנו את כל 28 הבעיות, המדידות דיברו בעד עצמן:
- זמן תגובת ה-API הוכפל לטובה — כלומר, ירד בחצי בממוצע
- מספר הבקשות הרשתיות מה-frontend ירד ב-60%
- הדשבורד מרגיש מיידי — גם עם מאות שיחות פעילות
- תלונות על ביצועים מלקוחות — פסקו לחלוטין
אבל מעבר למספרים, משהו אחר השתנה: הצוות שלנו התחיל לדבר על ביצועים כחלק מהשגרה. כל פיצ'ר חדש שנבנה — שואלים: "מה ההשפעה על הביצועים?" לא כי מישהו ביקש, אלא כי ראינו בעיניים מה קורה כשלא שואלים.
למה זה חשוב ספציפית לפלטפורמת WhatsApp
ב-ReplyQ אנחנו בונים כלי שעוזר לעסקים לנהל תקשורת עם לקוחות דרך WhatsApp. הלקוחות שלנו — חנויות, שירותי לקוחות, צוותי מכירות — לא יכולים לחכות. כשנציג שירות ממתין שלוש שניות כדי לראות שיחה, הלקוח שלו כבר מאבד סבלנות. מהירות כאן היא לא תכונה אחת — היא הבסיס של כל הערך שאנחנו מספקים.
ובגלל זה ההשקעה באודיט הביצועים הזה לא הייתה "נחמד שעשינו" — היא הייתה הכרחית.
מה אנחנו לוקחים קדימה
החלטנו שאודיט ביצועים כזה יהפוך לתהליך קבוע — לא רק כשלקוח מתלונן. בכל רבעון נשב ונמדוד. נחפש את הפקק הבא. נתקן. כי כמו שגילינו — תמיד יש עוד 8 בעיות אחרי ה-20 הראשונות.
אם אתם מנהלים פלטפורמה עם API שמטפל בנפח גבוה של הודעות, אנחנו ממליצים לא לחכות ללקוח שיאמר לכם שיש בעיה. תקדימו אותו.
רוצים לראות את הדשבורד המהיר בפעולה? קבעו דמו חינמי ונראה לכם בדיוק איך זה נראה מבפנים.