# 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 לפני הקו הזה כבר לא יתקבלו, אפשר לסגור את החלון"

```mermaid
flowchart RL
    subgraph Tumbling["Tumbling Window — non-overlapping"]
        T1["00:00-00:01"] --> T2["00:01-00:02"] --> T3["00:02-00:03"]
    end
```

```mermaid
flowchart RL
    subgraph Sliding["Sliding Window — overlapping"]
        S1["00:00-00:05"]
        S2["00:01-00:06"]
        S3["00:02-00:07"]
    end
```

```python
# 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"))
```

```python
# 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](https://docs.confluent.io/platform/current/streams/concepts.html#windowing)

---

## תרגילים

### תרגיל 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 לפי-זמן.

**דרישות:**
1. קיבוץ אירועים בחלונות של דקה אחת (`dt.floor("min")`), עם ספירה וממוצע ערך לכל חלון
2. הוספת Watermark של 30 שניות — אירועים שמגיעים באיחור של יותר מ-30 שניות מתועדים כ-Late Event בלוג ולא נכללים בחלון שכבר נסגר
3. פרסום סיכום כל חלון (לא כל אירוע בודד) ל-Topic חדש, בסגנון הדוח היומי מיחידת ה-ETL

**בדיקה:** אירועים "בזמן" נכללים בחלון הנכון; אירוע מאוחר מכוון (מעל 30 שניות) מתועד כ-Late Event ולא משפיע על חלון שכבר נסגר; סיכום כל חלון מפורסם נכון עם ספירה וממוצע.

## מה בפרק הבא

בפרק הבא — **פרויקט מסכם סופי**: שילוב מלא של שלושת השירותים (Ingestion, Processing, Query) עם Inter-Service Communication — סיום הקורס כולו.
