mensiva.it

Watcher PRO: architettura, sviluppo e risoluzione del batch iniziale

Alt Uno snippet di codice Python è reso con un effetto prospettiva e colori al neon che risaltano sullo sfondo scuro, creando un'atmosfera tecnologica e d'impatto.

Illustrazione co-creata con Nano Banana 2

📘 La costruzione del Watcher PRO: un viaggio tecnico nella sincronizzazione tra filesystem, backend Laravel ed Electron


🧭 Introduzione

In questo articolo racconto — in modo tecnico e dettagliato — lo sviluppo del Watcher PRO, un componente fondamentale di un progetto più ampio: un assistente digitale avanzato, capace di monitorare in tempo reale un progetto Flutter/Dart, inviare eventi strutturati a un backend Laravel e sincronizzare la struttura del progetto con un’interfaccia Electron.

Il Watcher PRO è il cuore pulsante del sistema: osserva, interpreta, normalizza e comunica ogni modifica del filesystem, garantendo che l’assistente digitale sia sempre aggiornato sullo stato del progetto.

Quello che segue è il percorso completo:
dalla progettazione iniziale → alla gestione degli eventi → alla scansione iniziale → al batching → alla risoluzione dei problemi → alla stabilizzazione finale.


🏗️ Obiettivo del Watcher PRO

Il Watcher PRO deve:

  • monitorare in tempo reale una cartella (la root del progetto Flutter)
  • rilevare creazioni, modifiche e cancellazioni di file
  • normalizzare i percorsi
  • filtrare cartelle irrilevanti (build, .git, .dart_tool…)
  • inviare eventi al backend Laravel
  • gestire heartbeat periodici
  • inviare un batch iniziale con la struttura completa del progetto
  • mantenere sincronizzato Electron con la struttura del filesystem

Il tutto con:

  • robustezza
  • tolleranza agli errori
  • assenza di blocchi
  • assenza di duplicazioni
  • assenza di perdita di eventi

🔍 Architettura del Watcher

Il Watcher PRO è composto da:

1. event_queue

Una coda thread-safe (queue.Queue) dove confluiscono tutti gli eventi:

  • eventi del filesystem
  • heartbeat
  • eventi di startup
  • eventi di errore
  • eventi della scansione iniziale
  • evento “flush” (patch finale)

2. sender_thread

Il thread responsabile dell’invio degli eventi al backend Laravel.

Funzioni principali:

  • consuma la coda
  • gestisce heartbeat separatamente
  • accumula eventi in un buffer
  • invia batch ogni 100 ms
  • gestisce errori interni
  • invia un batch completo quando riceve “flush”

3. heartbeat_thread

Invia un evento “alive” ogni 30 secondi, utile per:

  • monitorare lo stato del watcher
  • evitare timeout lato backend
  • garantire che il watcher sia vivo anche in assenza di modifiche

4. ProEventHandler (Watchdog)

Gestisce gli eventi del filesystem:

  • on_created
  • on_modified
  • on_deleted

Filtra cartelle ignorate e normalizza i percorsi.

5. initial_scan

Scansiona l’intera root del progetto e genera:

  • folder_created per ogni cartella
  • file_created per ogni file

Serve a costruire la struttura iniziale del progetto.

6. flush (patch finale)

Un evento artificiale che forza l’invio del batch completo dopo la scansione iniziale.


🧪 Il problema che abbiamo incontrato

Durante lo sviluppo, il Watcher PRO funzionava perfettamente:

  • rilevava eventi
  • inviava heartbeat
  • inviava batch
  • sincronizzava Electron
  • popolava project_structure.json

Poi, improvvisamente:

  • heartbeat continuava a funzionare
  • la scansione iniziale veniva eseguita
  • gli eventi venivano generati
  • il sender_thread riceveva gli eventi
  • ma nessun batch veniva inviato

Laravel mostrava:

Batch check: {"has_batch":false}

Electron non riceveva nulla.
La struttura non veniva aggiornata.


🧩 Diagnosi tecnica

Grazie a una riga di debug aggiunta nel punto giusto:

print("[DEBUG] Evento nel sender_thread:", event)

abbiamo scoperto che:

  • il sender_thread riceveva correttamente tutti gli eventi
  • il buffer si riempiva
  • ma il batch non scattava mai

Perché?

🔥 Perché gli eventi della scansione iniziale arrivavano troppo velocemente.

Il batching era basato su:

if time.time() - last_send > 0.1:

Ma:

  • centinaia di eventi arrivavano in meno di 100 ms
  • il timer non scattava
  • il batch non veniva inviato
  • il buffer non veniva svuotato
  • Laravel non riceveva nulla

🛠️ La soluzione: evento “flush”

Abbiamo introdotto un evento artificiale:

event_queue.put({"type": "flush"})

inviato subito dopo la scansione iniziale.

E nel sender_thread:

if event.get("type") == "flush":
    if buffer:
        send_event({"batch": buffer})
    buffer = []
    event_queue.task_done()
    continue

Questo:

  • forza l’invio del batch completo
  • svuota il buffer
  • sincronizza Laravel
  • sincronizza Electron
  • ripristina il comportamento originale del watcher

🚀 Risultato finale

Electron ha mostrato:

[Watcher] batch_summary batch:334

Laravel ha ricevuto la struttura completa.
project_structure.json è stato popolato.
Il watcher è tornato stabile e affidabile.


📦 Stato attuale del Watcher PRO

Oggi il Watcher PRO:

  • monitora correttamente il filesystem
  • invia heartbeat regolari
  • gestisce errori interni
  • invia batch ogni 100 ms
  • invia un batch completo iniziale tramite flush
  • mantiene sincronizzato Laravel
  • mantiene sincronizzato Electron
  • è robusto, stabile e pronto per essere integrato nell’assistente digitale

🔮 Prossimi passi

In futuro potremo:

  • ottimizzare il flush (solo se necessario)
  • ridurre la frequenza dei batch
  • introdurre un sistema di differenze (diff)
  • aggiungere un watcher per i file di configurazione
  • integrare un sistema di caching
  • collegare il watcher al motore di analisi del codice dell’assistente digitale

🏁 Conclusione

Il Watcher PRO è ora un componente solido, affidabile e pronto per essere integrato nel sistema più grande: un assistente digitale capace di comprendere il progetto Flutter, analizzarlo, proporre refactoring, generare codice e mantenere una memoria contestuale.

Questo articolo racconta l’intero percorso:
dalla progettazione → al problema → alla diagnosi → alla soluzione → alla stabilizzazione.

credit: sistema sviluppato con assistenza tecnica di Microsoft Copilot


📚 Bibliografia essenziale per la realizzazione di un Watcher in Python

La costruzione di un watcher affidabile in Python richiede la comprensione di alcuni strumenti fondamentali: il modulo watchdog, la gestione dei thread, le code FIFO (queue.Queue) e le tecniche di batching.
Di seguito una selezione di risorse essenziali, autorevoli e direttamente utili allo sviluppo del Watcher PRO.


🔹 Documentazione ufficiale Watchdog

Watchdog — Python API Documentation

https://python-watchdog.readthedocs.io/en/stable/

La risorsa più importante.
Contiene:

  • API complete di Observer e FileSystemEventHandler
  • esempi di implementazione
  • gestione dei thread interni
  • note sulle differenze tra OS (Windows, macOS, Linux)
  • limitazioni note del backend di Watchdog

🔹 Repository ufficiale su GitHub

Watchdog GitHub Repository

https://github.com/gorakhargosh/watchdog

Utile per:

  • leggere il codice sorgente
  • comprendere il comportamento interno degli observer
  • verificare issue note e bug reali
  • studiare esempi avanzati non presenti nella documentazione

🔹 Python Threading (documentazione ufficiale)

Threading — Python Standard Library

https://docs.python.org/3/library/threading.html

Fondamentale per:

  • comprendere la gestione dei thread del watcher
  • evitare deadlock
  • gestire correttamente thread multipli (sender, heartbeat, observer)
  • capire come funziona daemon=True

🔹 Python Queue (documentazione ufficiale)

Queue — Python Standard Library

https://docs.python.org/3/library/queue.html

Indispensabile per:

  • implementare una coda thread-safe
  • evitare race condition
  • gestire correttamente put(), get(), task_done()
  • costruire un sistema di batching affidabile

🔹 Requests — HTTP client per Python

Requests Documentation

https://requests.readthedocs.io/en/latest/

Utile per:

  • inviare eventi al backend Laravel
  • gestire errori di rete
  • implementare retry e timeout
  • costruire payload JSON robusti

🔹 Esempio di batching manuale (concetto base)

Il batching del Watcher PRO si basa su un pattern simile a questo:


import queue, time

event_queue = queue.Queue()
buffer = []
last_send = time.time()

while True:
    event = event_queue.get()
    buffer.append(event)

    if time.time() - last_send > 0.1:
        print("Invio batch:", buffer)
        buffer = []
        last_send = time.time()

    event_queue.task_done()

Questo esempio mostra:

il concetto di buffer

il timer per l’invio

la gestione della coda

la logica di batching

Il Watcher PRO implementa una versione molto più avanzata, con:

heartbeat

flush

gestione errori

thread multipli

normalizzazione dei path

filtraggio delle directory

integrazione con Laravel ed Electron