ARCH-REPORT: Il “Caso Split-Brain” nella Pipeline Dati

Data: 2025-12-09 Area: Core Data Pipeline / State Management Stato: Analisi Completata In attesa di esecuzione PAD Priorità: Critica (Bloccante per la feature “Smart Data”)

Vedi il dettaglio nella cartella apposita!


1. Il Contesto Operativo: La Visione “Smart Data”

Per garantire la sovranità dei dati e l’accuratezza dell’analisi tecnica in FIRE, abbiamo implementato una nuova architettura di gestione dei dati storici (Soluzione 2: Smart & Metadata).

Il concetto:

  1. Storage: I dati vengono salvati su disco sempre in formato RAW (Grezzo, non rettificato), accompagnati da un file “sidecar” .meta.json contenente gli eventi (Split e Dividendi).
  2. Runtime: Un toggle nella UI (“ADJ/RAW”) permette all’utente di scegliere come visualizzare il dato.
  3. Calcolo On-The-Fly: Se l’utente sceglie “ADJ”, il DataManager applica matematicamente la rettifica al volo. Se sceglie “RAW”, serve i dati grezzi.

2. L’Incidente (Il Sintomo)

Durante i test di validazione (Test Case: NVDA), è emerso un comportamento anomalo:

  • Azione: L’utente preme il toggle “RAW” nella Toolbar.
  • Aspettativa: Il grafico si ricarica mostrando il prezzo storico non rettificato (~$1000).
  • Realtà: Il grafico si ricarica, ma mostra ancora il prezzo rettificato (~$95).
  • Osservazione: Chiudendo e riavviando l’applicazione con il toggle su “RAW”, il grafico appare corretto.

Diagnosi Funzionale: Il sistema funziona “a freddo” (avvio), ma fallisce “a caldo” (runtime).


3. Root Cause Analysis (RCA): Il Problema “Split-Brain”

L’analisi del codice ha rivelato un difetto strutturale nella gestione delle dipendenze (Dependency Injection), creando una situazione di “cervello diviso”.

Lo Scenario Attuale (Errato)

Il componente SettingsManager (che detiene il flag use_adjusted_prices) è stato istanziato più volte in parti diverse dell’applicazione che non comunicano tra loro.

graph TD
    A[Main Window] --> B[AppState]
    A --> C[PanelFactory]
    
    B -- Crea internamente --> D(SettingsManager ISTANZA A)
    C -- Crea internamente --> E(SettingsManager ISTANZA B)
    
    D --> F[UI Toggle]
    E --> G[DataManager] --> H[Worker]
    
    style D fill:#ffcccc,stroke:#333,stroke-width:2px
    style E fill:#ccffcc,stroke:#333,stroke-width:2px
  1. L’Istanza A (UI): Quando l’utente clicca il toggle, AppState aggiorna la sua istanza (Istanza A) in memoria.
  2. L’Istanza B (Worker): Il DataManager (creato da PanelFactory) possiede una diversa istanza (Istanza B).
  3. Il Cortocircuito: Poiché SettingsManager non era un Singleton, l’aggiornamento in memoria su A non si rifletteva su B. Il Worker continuava a leggere il vecchio valore (False) dall’Istanza B finché l’app non veniva riavviata (rilettura da disco).

4. La Sfida Architetturale: Thread Safety e Coerenza

Risolvere questo problema non è banale perché ci muoviamo in un ambiente Multithreaded (Qt).

Il Rischio di Race Condition: Anche unificando le istanze, se un Worker asincrono legge lo stato globale (SettingsManager.use_adjusted) mentre l’utente lo sta modificando dalla UI, si crea una condizione di gara (Race Condition) che può portare a comportamenti indeterminati.

L’Innovazione Necessaria: Il Pattern “Snapshot” Indipendentemente dalla strategia di unificazione scelta, è emersa la necessità vitale di disaccoppiare il Worker dallo stato globale mutevole. Il Worker deve ricevere una “Fotografia” (Snapshot) della configurazione al momento della sua nascita (passaggio di valore booleano nel costruttore) e ignorare qualsiasi cambiamento successivo.


5. Le Vie d’Uscita (Strategie Valutate)

Per correggere l’architettura, abbiamo elaborato tre strategie distinte, ognuna con un diverso equilibrio tra purezza del codice e pragmatismo.

  1. Strategia #1: Dependency Injection Pura (Purista)

    • Concetto: Creare un’unica istanza nel main.py e passarla a cascata a tutti i costruttori.
    • Contro: Richiede un refactoring massivo (“Big Bang”) di file critici.
  2. Strategia #2: Singleton Pattern (Pragmatica)

    • Concetto: Trasformare SettingsManager in una variabile globale accessibile ovunque.
    • Contro: Introduce stato globale nascosto (“Magia”), rendendo i test più difficili.
  3. Strategia #3: Targeted Instance Sharing (Equilibrata)

    • Concetto: Sfruttare il fatto che AppState possiede già l’istanza corretta e aggiornata, e passarla esplicitamente al DataManager tramite la PanelFactory.
    • Pro: Risolve il bug con modifiche chirurgiche (pochi file), senza introdurre Singleton e senza riscrivere l’intero main.py.

6. Conclusione

Sulla base di questa analisi, il team ha deciso di procedere con la definizione di un Piano d’Azione Dettagliato (PAD) per implementare la soluzione scelta, garantendo:

  1. Unificazione dello Stato: UI e Worker devono vedere la stessa verità.
  2. Sicurezza dei Thread: Utilizzo del pattern Snapshot.

Segue il PAD specifico per la strategia selezionata.