Stream Processing מתקדם — Windowing, Late Events, Watermarks
מה זה?
ב-Analyzer Service (מהפרויקט הקודם) השתמשנו ב-deque(maxlen=100) — חלון לפי כמות אירועים אחרונים. אבל “כמה אירועים קרו בכל דקה?” דורש חלון לפי זמן, לא כמות — Windowing פותר את זה. ומה קורה כשאירוע מגיע מאוחר (רשת איטית, ניתוק זמני) אחרי שהחלון שלו כבר “נסגר”? Late Events ו-Watermarks הם הכלים המתקדמים להתמודד עם המציאות המבולגנת הזו.
מילות מפתח שחשוב לזכור
• Tumbling Window (חלון קופצני) — חלונות זמן קבועים ולא-חופפים: 00:00-00:01, 00:01-00:02 וכו’ — כל אירוע שייך לחלון אחד בדיוק
• Sliding Window (חלון נגלל, ברמת זמן) — חלון שנע ברציפות, יכול לחפוף לחלון הקודם (למשל “5 הדקות האחרונות”, מתעדכן כל שנייה)
• Late Event (אירוע מאוחר) — אירוע שמגיע אחרי שהחלון “שלו” (לפי הזמן שהוא קרה, לא הזמן שהוא הגיע) כבר נסגר ועובד
• Watermark — “קו זמן” שאומר למערכת “אירועים עם timestamp לפני הקו הזה כבר לא יתקבלו, אפשר לסגור את החלון”
flowchart RL
subgraph Tumbling["Tumbling Window — non-overlapping"]
T1["00:00-00:01"] --> T2["00:01-00:02"] --> T3["00:02-00:03"]
end
flowchart RL
subgraph Sliding["Sliding Window — overlapping"]
S1["00:00-00:05"]
S2["00:01-00:06"]
S3["00:02-00:07"]
end
# Basic Tumbling Window — grouping events by minute, with Pandas
import pandas as pd
def tumbling_window_minute(events_df):
events_df["window"] = pd.to_datetime(events_df["timestamp"]).dt.floor("min") # round down to the minute
return events_df.groupby("window").agg(count=("eventId", "count"), avgValue=("value", "mean"))# Late Event + Watermark — handling events that arrive late
watermark = pd.Timestamp.now() - pd.Timedelta(minutes=2) # allow a delay of up to 2 minutes
def handle_event(event, watermark):
event_time = pd.to_datetime(event["timestamp"])
if event_time < watermark:
logging.warning(f"Late Event rejected: {event['eventId']}, time {event_time} before watermark {watermark}")
return False # arrived too late, the window is already closed
return True # accepted for regular processingהסבר עיקרי
Tumbling מול Sliding — מתי כל אחד — Tumbling (חלונות נפרדים, לא-חופפים) מתאים ל"דוח לכל דקה בדיוק" — כל אירוע שייך לחלון אחד ויחיד, ברור ופשוט לחישוב מצטבר (כמו groupby על עמודת דקה מעוגלת). Sliding (חלון נע וחופף) מתאים ל"ממוצע נגלל של 5 הדקות האחרונות, מתעדכן כל שנייה" — יותר “חלק” אך דורש יותר חישוב, כי כל אירוע חדש משפיע על כמה חלונות בו-זמנית.
Late Events הם מציאות בלתי-נמנעת בזרמי נתונים — ברשת אמיתית, הודעה שנשלחה ב-10:00:00 לא בהכרח מגיעה ב-10:00:00 — יכולה להתעכב (רשת איטית, ניתוק זמני) ולהגיע ב-10:00:05, אחרי שהחלון “10:00:00-10:00:01” כבר נחשב סגור ומעובד. ההבדל בין “זמן שהאירוע קרה” (Event Time) ל"זמן שהוא הגיע לעיבוד" (Processing Time) הוא בדיוק מה ש-Late Events מטפלים בו.
Watermark הוא פשרה מודעת, לא פתרון מושלם — לחכות לנצח לכל אירוע מאוחר אפשרי אומר שאף חלון לעולם לא “נסגר” סופית — לא מעשי. Watermark קובע גבול סביר (“נקבל איחורים של עד 2 דקות”) — אחריו, החלון נסגר וסופר כ"סופי", גם במחיר דחיית אירועים שמגיעים מאוחר מדי. זו בדיוק אותה החלטת Trade-off כמו TTL על קאש — פשרה מודעת בין דיוק מוחלט לתפקוד מעשי.
יתרונות
Tumbling Window נותן דוחות ברורים ולא-חופפים, קלים לחישוב; Sliding Window נותן תמונה “חיה” ומתעדכנת ברציפות; Watermark מאפשר לסגור חלונות סופית בזמן סביר, בלי לחכות לנצח לכל איחור אפשרי.
חסרונות
Late Events שנדחים אחרי ה-Watermark פשוט “אובדים” מבחינת אותו חלון — פשרה מכוונת, לא באג, אך דורשת החלטה עסקית מודעת; Sliding Window דורש יותר משאבי חישוב מ-Tumbling, כי כל אירוע משפיע על כמה חלונות.
נקודות חשובות
• Tumbling = חלונות קבועים לא-חופפים; Sliding = חלון נע, יכול לחפוף
• Late Event = אירוע שמגיע אחרי שהחלון שלו (לפי Event Time) כבר נסגר
• Watermark = גבול זמן סביר שאחריו חלון נסגר סופית, למרות אפשרות לאיחורים נוספים
• Event Time (מתי זה קרה) שונה מ-Processing Time (מתי זה עובד) — ההבדל הוא שורש בעיית ה-Late Events
טעויות נפוצות
• להשתמש ב-Processing Time (מתי ההודעה התקבלה) במקום Event Time (מתי היא קרתה בפועל) לחישוב חלונות — תוצאות שגויות עם רשת לא-אמינה
• לצפות ש-Watermark “יפתור” איחורים לגמרי — הוא רק קובע פשרה סבירה, לא ערבות מושלמת
• לבחור Sliding Window כברירת מחדל בלי לשקול את עלות החישוב הנוספת מול Tumbling
סיכום
Tumbling Window מחלק זמן לחלונות קבועים ולא-חופפים; Sliding Window נע ברציפות ויכול לחפוף, נותן תמונה “חיה”. Late Events (אירועים שמגיעים אחרי שהחלון שלהם נסגר, בגלל פער בין Event Time ל-Processing Time) הם מציאות בלתי-נמנעת ברשת אמיתית. Watermark קובע גבול איחור סביר שאחריו חלון נסגר סופית — פשרה מודעת, לא פתרון מושלם, בדיוק כמו TTL על קאש. אלה הכלים המתקדמים שהופכים Stream Processing בסיסי (כמו ה-Sliding Window הפשוט מהפרויקט הקודם) לתשתית תעשייתית אמיתית.
דוקומנטציה רשמית
Confluent — Windowing in Kafka Streams
תרגילים
תרגיל 1 — Tumbling Window
המשימה: מתוך DataFrame עם timestamps מפוזרים על פני כמה דקות, קבצו לפי דקה (dt.floor("min")) וחשבו ספירה וממוצע לכל חלון.
בדיקה: כל דקה מוצגת כחלון נפרד עם הספירה והממוצע הנכונים, בלי חפיפה בין חלונות.
תרגיל 2 — זיהוי Late Event
המשימה: כתבו פונקציה שמקבלת אירוע ו-Watermark, ומחזירה האם האירוע “מאוחר מדי” (Event Time לפני ה-Watermark).
בדיקה: אירוע עם timestamp אחרי ה-Watermark מתקבל; אירוע עם timestamp לפני ה-Watermark נדחה ומתועד ב-log.
פרויקט מסכם
המשימה: שדרגו את Analyzer Service (מהפרויקט הצוותי הקודם) מ-Sliding Window לפי-כמות ל-Tumbling Window לפי-זמן.
דרישות:
- קיבוץ אירועים בחלונות של דקה אחת (
dt.floor("min")), עם ספירה וממוצע ערך לכל חלון - הוספת Watermark של 30 שניות — אירועים שמגיעים באיחור של יותר מ-30 שניות מתועדים כ-Late Event בלוג ולא נכללים בחלון שכבר נסגר
- פרסום סיכום כל חלון (לא כל אירוע בודד) ל-Topic חדש, בסגנון הדוח היומי מיחידת ה-ETL
בדיקה: אירועים “בזמן” נכללים בחלון הנכון; אירוע מאוחר מכוון (מעל 30 שניות) מתועד כ-Late Event ולא משפיע על חלון שכבר נסגר; סיכום כל חלון מפורסם נכון עם ספירה וממוצע.
מה בפרק הבא
בפרק הבא — פרויקט מסכם סופי: שילוב מלא של שלושת השירותים (Ingestion, Processing, Query) עם Inter-Service Communication — סיום הקורס כולו.