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:
- Storage: I dati vengono salvati su disco sempre in formato RAW (Grezzo, non rettificato), accompagnati da un file “sidecar”
.meta.jsoncontenente gli eventi (Split e Dividendi). - Runtime: Un toggle nella UI (“ADJ/RAW”) permette all’utente di scegliere come visualizzare il dato.
- Calcolo On-The-Fly: Se l’utente sceglie “ADJ”, il
DataManagerapplica 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
- L’Istanza A (UI): Quando l’utente clicca il toggle,
AppStateaggiorna la sua istanza (Istanza A) in memoria. - L’Istanza B (Worker): Il
DataManager(creato daPanelFactory) possiede una diversa istanza (Istanza B). - Il Cortocircuito: Poiché
SettingsManagernon 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.
-
Strategia #1: Dependency Injection Pura (Purista)
- Concetto: Creare un’unica istanza nel
main.pye passarla a cascata a tutti i costruttori. - Contro: Richiede un refactoring massivo (“Big Bang”) di file critici.
- Concetto: Creare un’unica istanza nel
-
Strategia #2: Singleton Pattern (Pragmatica)
- Concetto: Trasformare
SettingsManagerin una variabile globale accessibile ovunque. - Contro: Introduce stato globale nascosto (“Magia”), rendendo i test più difficili.
- Concetto: Trasformare
-
Strategia #3: Targeted Instance Sharing (Equilibrata)
- Concetto: Sfruttare il fatto che
AppStatepossiede già l’istanza corretta e aggiornata, e passarla esplicitamente alDataManagertramite laPanelFactory. - Pro: Risolve il bug con modifiche chirurgiche (pochi file), senza introdurre Singleton e senza riscrivere l’intero
main.py.
- Concetto: Sfruttare il fatto che
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:
- Unificazione dello Stato: UI e Worker devono vedere la stessa verità.
- Sicurezza dei Thread: Utilizzo del pattern Snapshot.
Segue il PAD specifico per la strategia selezionata.