# Signal Shingle: una nuova architettura per siti di high-performance ASP.NET multi

<!-- category -- AI,Architecture,ASP.NET,Performance,Patterns,StyloBot -->
<datetime class="hidden">2026-07-24T12:00</datetime>

**Come si possono fornire risposte veloci per dashboard complessi senza diventare pazzi? Si smette di renderle sulla strada della richiesta.**

[TOC]

---


## Il problema: perM SK1request fan-out

Un dashboard è un tipo di pagina poco costoso.. Sembra una URL, ma è davvero un motore di composizione che indossa una pagina.

![Un dashboard implementato: contatori di riassunto , un grafico temporale- una serie di graficiM SK3 lista dei botMSC4 visualizzazioni di punti di fine e paesi condividono tutte una pagina mantenendo delle superfici indipendenti per l'updateMST5](/articleimages/signal-shingle-dashboard.jpeg)

L'operatore vede un pannello di controllo; il server vede lenti- aggregazioni mobiliM SK2 liste in diretta e visioni costose con finestreMSC3

```mermaid
flowchart TB
    P[One dashboard page] --> H[Headline counters]
    P --> T[Time-series chart]
    P --> B[Top bots]
    P --> E[Top content pages]
    P --> C[Countries / filters]
    H --> M[Shared hydrated page model]
    T --> M
    B --> M
    E --> M
    C --> M
    M --> S[Versioned widget shingles]
    S --> U[Independent HTMX OOB updates]

    classDef outline stroke:#cbd5e1,stroke-width:1.5px;
    classDef emphasis stroke:#38bdf8,stroke-width:2px;
    class P,H,T,B,E,C,M,S,U outline;
    class M,S,U emphasis;
    linkStyle default stroke:#94a3b8,stroke-width:1.3px;
```

L'implementazione familiare ASP.NET ha ogni widget che prende i propri dati, inizia quei richiedi in paralleloM SK2 poi aspetta per tutti prima di rendere la pagina .

```mermaid
flowchart LR
    R[One page request] --> W1[Summary query]
    R --> W2[Time-series query]
    R --> W3[Countries query]
    R --> W4[Endpoints query]
    R --> W5[Bot query]
    W1 --> J[Task.WhenAll]
    W2 --> J
    W3 --> J
    W4 --> J
    W5 --> J
    J --> V[Render response]

    classDef outline stroke:#cbd5e1,stroke-width:1.5px;
    classDef danger stroke:#fb7185,stroke-width:2px;
    class R,W1,W2,W3,W4,W5,V outline;
    class J danger;
    linkStyle default stroke:#94a3b8,stroke-width:1.3px;
```

Sembra veloce con una sola persona e un piccolo set di dati : nove chiamate indipendenti si sovrappongono , così la pagina impiega più o meno quanto il suo widget più lento piuttosto che la loro somma. L'arithmetica cambia quando la pagina diventa popolare

Sulla [Stylo.Bot](https://stylo.bot) dashboard, una carica normale di pagina significava circa nove chiamate parallele in tutto il sitoM SK1 confine di accesso. La visuale del giorno poteva scansionare più di un milione di linee . Una sola carica silenziosa era già multipleMST5secondoM ST6 Nel traffico concurrenteMst7 la pMSt8 è passata oltre sette secondiM st9 perché la pagina stava aspettando `Task.WhenAll` di circa dieci operazioni costose e ogni visitatore stava iniziando un'altra decina.

Il paralelismo è utile. PerM SK1fan della richiesta-è fuori il problema . A cinque spettatori simultaniMSC4 nove chiamate diventano quarantaMSL5 cinque chiamate competitiveMSR6 A 50 spetttori sono quattrocento e cinquedziesiMsl7 Il pool di connessioni alla base di dati vede una tempesta crescente di lavoro indipendentemente costosoMLS8 Le richieste rallentanoMSS9 mantengono le conneszioni più lungheM SS10 e rallentano ulteriormente le altre richiesteMSSS11

> Fan parallelo-out fa sembrare più veloce una singola richiesta moltiplicando la pressione che distrugge il sistema sotto carico.

Questa è la curva sbagliata di scala per un dashboard. Un dashboard è una superficie di osservazione condivisa. Dovrebbe diventare più economico quando più persone guardano la stessa cosa.

---


## La soluzione: Signal Shingle

Signal Shingle tratta ogni widget del pannello come un frammento di pagina pre-rended,autoM SK2contenutoMSC3 La sua identità sono i dati che rappresenta e come viene rendere MST4 widgetMSSK5 riquadro MST6 finestraMTS7 filtri e parametri di displayMSS8 La cadenza di refresh deliberatamente non fa parte di quell'identitàM SS9

La sua impronta digitale è semplice. `widget + normalised parameters + data-surface generation`. Un'update del paese non dovrebbe invalidare un liveM SK1carta per i visitatori ; un filtro cambiato non dovrebbe rendere freddo ogni altro widget. Una nuova generazione solo ri-

Questi frammenti vivono in un archivio LFU confinato. Lecture calde mantengono i widget guardati residenti; unoM SK2 le combinazioni di filtri spenti si perdono naturalmente .

SignalR dice che la superficie è cambiata; il cliente chiede il lotto di widget infettato; HTMX applica i frammenti di banda restituitiM SK2of -con IdiomorphMSC4 Il segnale porta un segnale sporcoMNK5 mai una copia dei dati del pannello di controlloMRK6

Nel modello, **Il segnale.** significa “considerare il rifrescoM SK1 **gioiello** è il risultato indipendentemente indirizzabile.

Rendere- Solo i segnali laterali sono insufficienti. Se ogni errore provoca un nuovo scansione della base di dati , il lavoro costoso rimaneM SK3 Il segnale Shingle usa due cache di livelloMSC4 con una mano deliberatamente strettaMST5 fuori tra loroMst6

```mermaid
flowchart LR
    D[Gateway<br/>authoritative events] --> B[Incremental window buckets<br/>data stays single-source]
    B --> C[Level 1 : content cache<br/>hydrated page result]
    C --> S[Level 2 : shingle cache<br/>versioned OOB widget HTML]
    S --> R[Request path<br/>read + Razor shell]

    T[Schedule tick/poll + LFU demand] --> W[Bounded prewarm waves]
    W --> B
    W --> C
    X[Coalesced SignalR dirty beacon] --> U[HTMX batch update]
    U --> S

    classDef outline stroke:#cbd5e1,stroke-width:1.5px;
    classDef emphasis stroke:#38bdf8,stroke-width:2px;
    class D,B,C,S,R,T,W,X,U outline;
    class C,S,W emphasis;
    linkStyle default stroke:#94a3b8,stroke-width:1.3px;
```

---


## I due livelli di cache : i dati restano affidabili, la presentazione rimane pronta

La distinzione è tra un **Il modello.** e le sue rappresentazioni. **Unità di consegna per i widget**. Nessun cache diventa un'autorità per i dati di rilevamento.

### Layere 1 : bocche incrementali

La porta possiede i dati. Tene una proiezione LFU limitataM SK1 nella memoria - di bocche a finestra incrementaleMSC3 con 30- ritentioni di giorno gonfianti SSK5 un set di lavoroMNK6 non un secondo databaseMRK7

Utilizzate bocche fine per sei diagrammi di ore-hour e 24-houre chartsM SK2 rollups horali per le lunghe finestre . Il punto è che una finestra richiesta fonde i bocchi piuttosto che riMSC4 leggere gli eventi grezziMNK5

```text
last 24 hours  → merge 288 five-minute buckets
last 30 days   → merge 720 hourly buckets

not

last 30 days   → scan and aggregate 1,000,000+ event rows again
```

Le nuove osservazioni aggiornano il foglio più recente. La parte storica non è ri-scannata-perche' una pagina è stata caricataM SK2 Il gateway rimane l'unico scrittore e il solo posto che decide cosa significhi una rilevazione .

### Il livello del cache 1 : il cache del contenuto idrata.

Innalzamento, il sito cache modelli di pannello idroidrico, chiaveto da un set di widget normalizzatoM SK2 finestra , filtri e generazioneMSC4 Ripara i dati e la visualizzazione autoritariMST5

La memorizzazione di un modello piuttosto che di un HTML completo mantiene i valori CSRF, cromo di autenticazione, bandiere e nonce delle caratteristiche sulla richiestaM SK2shell restituito dove appartengono .

Su una tastiera fredda, restituire uno stato di riscaldamento chiaro piuttosto che ricomputare in modo sincronizzato. Solo il materializzatore del ticco guadagna un nuovo risultato di paginaM SK2 La lettura successiva serve la più recente generazione , anche se una nuova è in voloMSC4

### Il livello del cache 2 : il cache di rendere shingle

Il pannello è deliberatamente diviso : il gateway possiede i dati; il sito possiede Razor e la relazione con il browserM SK2 il livello 2 fornisce piccole aggiornazioni localmente senza diventare un database competitivoMSC4 contiene solo versioni HTML derivate dal gatewayMST5 modello di pagina riservataMst6

Una volta che c'è un modello di pagina, un widget può essere rappresentato come elemento OOB e tenuto nel cachinglo limitato LFU con una sicurezza TTL. Un shingle caldo supera sia la composizione che il rendering RazorM SK2 un errore legge il risultato di una pagina calda già - il risultato della pagina calda dove è possibileMSC4 renderà solo quel widget `hx-swap-oob="morph"`, e immagazzina il risultato sotto la propria impronta digitale

La partizione finale del lotto divide i widget in appunti caldi e mancanze, compone le mancanze supportate una voltaM SK1 poi restituisce l'OOB HTML. Un riassunto o il cambiamento dei paesi reMSC3 tasti solo su quella superficie .

Questo non è cachingo ridondante. Niveau 1 risposte “ qual è il modello coerente del pannello di controlloM SK3 Niveau

---


## Limito del cache: perché questo non è il caching di risposta

Il caching della risposta vale la pena confrontarlo direttamente. Un caching normale della risposta conserva una risposta HTTP opaca; i widget cacheti indipendenti sviluppano modi di expirazione e di fallimento indipendebiliM SK2 La pagina può essere caricata velocemente senza più descriver uno stato coerenteMSC3 Su un pannello operativoMska4 un conteggio che non è d'accordo con le linee sotto fa sì che l'operatore investisca gli fantasmaMske5

I modelli di caching familiari sono tutti utili. Si proteggono semplicemente diversi confini:

| Tipo | Ciò che si conserva | | La migliore adattamento || Perché è insufficiente da solo qui |
|---|---|---|---|
| Cache di risposta HTTP | Una risposta HTTP, normalmente segnata con l'URL e la richiestaM SK3 regole diverse | | Finepoint GET pubblici e CDN |- | Contenuto amichevole | МSK6 | Non sa niente del pannello di controllo | ' | dei widget interni o se i loro dati sono d'accordo |
| ASPM SK1Cache output NET | Output generato da un server, con una politica esplicita di variazione e di estinzione || pagine costose con una piccola | , richiesta stabile |
| Donut-foro /cachezza di frammenti || Un guscio cache con buchi dinamici | , o frammenti cacheti indipendenti | | | pagine il cui cromo è stabile e le cui piccole regioni dinamiche sono veramente indipendente ♫| Fa sì che ogni buco abbia un suo ciclo di vita del cache | МSK7 è esattamente come un pannello può diventare interamente inconsistente
| Signal Shingle | Una generazione idrataM SK2-modello di pagina stampata più frammenti di widget versioniati | Un pannello i cui componenti devono essere aggiornati in modo indipendente **e** Continuare a descrivere uno stato | Il confine del cache è la proiezione compiuta; i segnali sono derivati da essoM SK2 non negozi competitiviMSC3 |

Nivello-2 schianchi *Si tratta di un sistema multifunzionale.* un archivio di output, deliberatamente piccolo e frammentato-con la forma di . La differenza è che i frammenti non decidono la realtà dei dati in modo indipendenteM SK3 Il modello del contenuto è composto primaMSC4 in una sola generazioneMSSK5 ogni scintilla è una rappresentazione di quel modello

```mermaid
flowchart LR
    D[One authoritative<br/>gateway dataset] --> G[Generation 184]
    G --> M[One hydrated<br/>page model]
    M --> H[Headline shingle<br/>generation 184]
    M --> T[Chart shingle<br/>generation 184]
    M --> B[Bot-list shingle<br/>generation 184]
    M --> C[Country shingle<br/>generation 184]
    H --> P[One coherent page]
    T --> P
    B --> P
    C --> P

    classDef outline stroke:#cbd5e1,stroke-width:1.5px;
    classDef emphasis stroke:#38bdf8,stroke-width:2px;
    class D,G,M,H,T,B,C,P outline;
    class G,M,P emphasis;
    linkStyle default stroke:#94a3b8,stroke-width:1.3px;
```

Quando i dati cambiano, la composizione colpita riceve una nuova generazioneM SK1 Un browser può vedere brevemente quella precedente mentre un'update è in volo, ma quell'età è esplicita e limitata . Non è mai un insieme separato di cache appartiene a - che fanno affermazioni contraddittorie su MSC5oraMNK6

**Un'autorità, una generazione complessa, molti frammenti di consegna riutilizzabiliM SK2**

---


## SSR primo, miglioramento graduale secondo

Signal Shingle non è un API JSON con un cliente-dispositivo laterale dipinto sopra la superficie.Il StyloM SK2Dispositivo del botto è il Razor SSR prima MSC3 contatori significativiMST4 graficiMSSK5 tavoleMSS6 link e navigazione prima che JavaScript funzioniMSL7 HTMXMSV8 SignalR e Alpine poi migliorano quella pagina piuttosto che la sostituireMSM9

```mermaid
flowchart LR
    R[GET dashboard] --> S[Razor SSR<br/>complete useful HTML]
    S --> V[Operator can read<br/>and navigate now]
    S --> H[HTMX enhancement]
    S --> A[Alpine enhancement]
    S --> G[SignalR enhancement]
    G --> D[Dirty beacon<br/>not dashboard JSON]
    D --> H
    H --> O[GET OOB widget batch]
    O --> W[Versioned HTML shingles]
    W --> M[HTMX / Idiomorph morph]
    A --> I[Local UI state<br/>menus, filters, affordances]

    classDef outline stroke:#cbd5e1,stroke-width:1.5px;
    classDef emphasis stroke:#38bdf8,stroke-width:2px;
    class R,S,V,H,A,G,D,O,W,M,I outline;
    class S,W,M emphasis;
    linkStyle default stroke:#94a3b8,stroke-width:1.3px;
```

HTMX chiede HTML e scambia l'isola restituita. SignalR fornisce un piccolo segnale sporco piuttosto che i modelli JSON . Alpine possiede lo stato locale dell'interfaccia : menusM SK3 toggles and affordances that do not deserve a roundMska4tripM Ska5 Remove any one of them and the SSR page remains usefulMske6 add all'addizione tutte e tre e si ottiene il tempo in direttaMSKA7 lowMSC8churn updatesMsek9

---


## Refresh è un lavoro programmato, non è un effetto collaterale accidentale del traffico

Refresh avviene al di fuori della richiesta di pagina. Questo è un confine rigido : una lettura fredda restituisce un risultato di riscaldamento , mentre solo il materializzatore tick componeM SK3 La richiesta di página è una lettura di una proiezione coerenteMSC4 non è mai una scusa per iniziare a lavorare con dati costosiMNK5

Le linee dei coordinatori sono piegate e recentemente-lasciate le buste, poi le riscalda in onde confinate con una paginaM SK2calcolo e parete -budget dell'orologioMSC4 In un'onda scappa il lavoro compatibileMST5 un budget di costi misurato può fermare completamente le onde più tardiMSP6 Questo non è `Task.WhenAll` indossando un nuovo nome.

Il limite di corrispondenza fa parte della correttezza : il circuito di rifresco ottiene un budget di capacità noto, mai una serie infinita di connessioniM SK2 Un'update completamente caldo non ha alcuna composizione o rendering Razor .

**Orchestrato, non una tempesta.** Questa è la regola operativa.

### Pre- riscaldamento è copertura, non cachingo desiderato

Un cache non può proteggere la prima richiesta se solo si scalda. *dopo.* che la richiesta arriva. Quindi il materializzatore ha due lavori. **Defaults Pinned** Tenere le scelte di finestra calde 6hM SK1 24h\, **Demand-enveloppe pagate** I filtri di copertura che le persone usano realmente, classificati secondo il numero di accesso e la recinzioneM SK1 Solo una richiesta vera ne mantiene una viva.

```mermaid
flowchart TB
    P[Pinned default windows<br/>6h · 24h · 7d · 30d] --> Q[Warm queue]
    L[Recently read filtered views<br/>LFU hotness order] --> Q
    Q --> G{Tick budget<br/>still available?}
    G -->|yes| W[Bounded warm wave<br/>compose once, store model]
    G -->|no| N[Defer to next tick<br/>serve prior generation]
    W --> H[Level 1 content cache]
    H --> Z[Level 2 shingle cache<br/>rendered on demand]

    classDef outline stroke:#cbd5e1,stroke-width:1.5px;
    classDef emphasis stroke:#38bdf8,stroke-width:2px;
    class P,L,Q,G,W,N,H,Z outline;
    class Q,W,H emphasis;
    linkStyle default stroke:#94a3b8,stroke-width:1.3px;
```

Il pre- riscaldamento deve corrispondere alla superficie reale del prodotto. Se l'interfaccia grafica offre quattro finestre normali e il riscaldamento ne sa unaM SK1 tre quarti della prima prevista-la strada di clic è ancora fredda .

---


## LFU non è solo l'evizione : è il segnale della domanda.

LFU è più che l'evizione qui. L'accesso è il segnale della domanda : decide cosa rimane residente , che il lavoro necessario rinfresca primaM SK3 e ciò che può scomparire in sicurezzaMSC4 Ciò rende maneggiabile lo spazio senza limiti del filtroMST5 Un filtro di paese guardato tutto il giorno rimane caldoMSV6 un filtro provato una volta vecchioM SV7 Non c'è separato MSCV8un pannello popolareM Sv9 c'é bisogno di una tavolaMsv10

---


## Adaptabile verso lo statico.

I traditionali pannelli live reagiscono a più traffico facendo più lavoro, diventano poi meno utili al punto in cui gli operatori ne hanno più bisogno. Signal Shingle inverte quella relazioneM SK2 Il controller di Refresh misura il costo del Refresh rispetto ad un budget ; cicli costosi trascinano intervalli efficaciMSC4 quelli più economici contraggono verso la cadenza richiestaMST5

```text
refresh cost <= budget  → preserve requested cadence
refresh cost >  budget  → lengthen effective cadence
cost settles            → cautiously shorten it again
```

Questo è un circuito chiuso sulla risorsa protetta, non è un'ipotesi da RPS grezzo. sotto carico il sistema si muove verso una foto scattata statica invece di creare più lavori di database per mantenere un'impossibile promessa di vitalità

> La carica fa funzionare meno il pannello di controllo, non piùM SK1 Ecco perché il modello si degrada invece di crollare.

“StaticoM SK1 non è un fallimento . È l'ultima proiezione coerente conosciuta M SK3 legata ad una politica di frescozza e etichettata onestamente .

### Un ibrido statico/dinamico, dal design

La metà statica è il contratto di risposta : un rapido snapshot , coherente dal livello del cache. La metà dinamica è un processo separato con un budget ♫: programmatore ♫ - guidato da controlli corretti ♫

Così il sistema può scivolare continuamente tra due modi utili:

```text
quiet / cheap refreshes       → dynamic dashboard, short effective cadences
busy / expensive refreshes    → static-like snapshot, longer declared cadences
```

Questo è scaldamento adattativo costruito nell'architettura, non c'è un interruttore di panico aggiunto dopo il fallimento della piscina.

---


## La frescozza appartiene al widget: una non-invariante négociabile

Non tutti i widget meritano la stessa ricchezza. Un grafico di trend o un ranking dei punti finali può essere indietro di pochi minuti o di un'oraM SK1 una lista di visitatori recenti o dei punteggi attuale ha bisogno di una cadenza più stretta. La cadenza vive con il widget ' la classe di ricquezza

| Classe | Widget tipici | Cadenza di base | Perché S|
|---|---|---:|---|
| Aggregato | riassuntoM SK2 tendenze, paesiMST4 punti finali | da minuti all'ora SSM6 Il segnale cambia abbastanza lentamente che la stabilità sia più preziosa di una agitazione.
| Live | visitatori recentiM SK2 sessione, nomiMST4 punteggiM ST5 minacce SST6 circa un minuto MSST7 L'operatore sta cercando il movimento attualeMSST8 SMST9

Due widget visibilmente identici possono chiedere diverse cadenze. Mettere la cadenza nella chiave del cache duplica i dati; accettare la prima cadenza può lasciare un consumatore più tardi in gambaM SK2

L'invariante è semplice:

> Un'entrata di cache condivisa non deve mai servire lo stampatore rispetto a uno dei suoi consumatori attivi che è stato richiesto.

Formalmente:

```text
effective cadence(entry) = MIN(requested cadence of every active consumer)
```

Il Cadence è un consumatore per -. *Il pavimento.*, non fa parte della firmaM SK1 Se un consumatore chiede 60 secondi e un altro per cinque minuti, una sola intrazione condivisa rinfresca ogni

---


## Regole di onestà sul cromo e sulla coerenza

Un sistema adattativo dovrebbe essere onesto riguardo alla sua adattamento. Il cromo del widget mostra la cadenza effettiva : “ aggiornando ogni 60sM SK4 \“\ aggiornando tutti i dati SSK6mMS”, | |

Ci sono alcune regole necessarie per mantenere una proiezione onesta:

- **Stampi di generazione.** Ogni chunk/shingle registra la generazione di dati che è stata costruita da. Un segnale sporco e una risposta possono essere confrontateM SK2 così un'update precedente non può sovrascrivere una visione più nuovaMSC3
- **Signali collegati.** Un rilevatore affollato può produrre molti eventi al secondo. Il percorso del segnale li stacca in un segnale sporco e una decisione di rifrescare invece che di sparare il browser o il circuito di rifriuso.
- **Resync al riconnessione.** Una riconnessione non viene trattata come prova che nulla è successo. Un atto dovuto-controllo chiude l'erroreM SK2l'intervallo di impulso deterministicoMSC3
- **Fallback TTL confinato.** Un frammento conservato non è autorizzato a diventare immortale. Una volta superato la classe-connesso ad un'impassezza specifica e incapace di rifrescarsiM SK2 diventa freddo / riscaldandosi piuttosto che sdraiando silenziosamente per sempre
- **Nessuna perdita di contenuto.** Live shingles possono contenere nomi, impronte digitali o punteggiM SK1 Il loro contenuto rimane nella memoria, non è registratoMSC3 e qualsiasi diagnosi li modifica di defaultMST4

L'obiettivo non è la coerenza istantanea. E' una proiezione la cui età e generazione sono limitateM SK1 osservabile e mai casuale.

---


## Problema e soluzione

Ecco l'intero commercio in una sola tavola.

| | PerM SK2fane di richiesta-out | Duale strata di segnale Shingle S|
|---|---|---|
| richiesta di pagina | Starts N data fetches and waits | Reads a ready model/chunk and renders the shell M|
| Lavoramento dei dati | Ripetizioni per visitatore | | | Shared |, | Incrementale
| Actualizzazione del widget | Ogni widget può essere caricato in modo indipendente | | | Structure calde servono direttamente |; | Passi compongono come lotti |
| Concorrenza  | `Task.WhenAll` Si espande con il traffico | Wave di rifresco limitate con un budget di costi misurato |
| Immagini filtrate | O non cache o senza confini SSK2 Combinazioni calde restano in direttaM SK3 quelle fredde LFU-evict |
| Rispondenza di carico | Più traffico crea più lavoro ♫| Più costi estengono la cadenza in direzione statica ♫ |
| Frischezza | Implicite e spesso inconsistente | Esplice per widgetM SK3 min-Sicurezza per le entrate condivise SSK5
| Modo di fallimento | Requisizioni lenti, esaurimento del poolM SK3 collasso ♫| Proiezione più vecchia ma etichettata ♫ , lavoro confinato ♫

Signal Shingle compone pezzi familiari di ASP.NET : un LFUM SK2 SignalR , composizione in battuteMST4 RazorMSC5 HTMX e un programmatore con un budget reale di corrispondenzaMSP6 Il gateway rimane autoritarioMSV7 il sito web fornisce una proiezioneM SV8 il browser riceve piccoli frammenti di versione modificataMsv9

Muovire il rendere dal percorso della richiesta. Lasciare lavorare il rifresco della bottiglia di carico piuttosto che la risposta.