# Mantieni i tuoi dati aggiornati

Una dashboard vale il suo URL solo se i numeri che mostra sono aggiornati. Questa è la parte che una sessione dell'agente non può lasciarsi alle spalle: il tuo agente costruisce la dashboard e se ne va, e i dati sottostanti devono continuare ad arrivare secondo una pianificazione, per mesi, senza che nessuno sia coinvolto.

La freschezza è quindi stato della piattaforma, non codice della dashboard. La configuri una volta su un dataset; continua a funzionare indipendentemente dal fatto che qualcuno tenga aperta una sessione.

## I tre modi in cui i dati avanzano[​](#i-tre-modi-in-cui-i-dati-avanzano "Link diretto a I tre modi in cui i dati avanzano")

|                                | Cos'è                                                  | Chi lo attiva                                                             |
| ------------------------------ | ------------------------------------------------------ | ------------------------------------------------------------------------- |
| **Aggiornamento su richiesta** | Ripreleva subito un dataset                            | Tu, il tuo agente, o la tua CI — `POST /v1/datasets/{dataset_id}/refresh` |
| **Caricamento incrementale**   | Preleva solo ciò che è cambiato dall'ultima esecuzione | La stessa chiamata, con `mode: "incremental"`                             |
| **Aggiornamento pianificato**  | Un'espressione cron che la piattaforma esegue per te   | Nessuno — il poller cron della piattaforma                                |

## Aggiorna subito un dataset[​](#refresh-a-dataset-now "Link diretto a Aggiorna subito un dataset")

```
result = client.datasets.refresh(dataset_id)

print(result.completed, result.effective_mode, result.message)
```

```
const result = await client.datasets.refresh(datasetId);

console.log(result.completed, result.effectiveMode, result.message);
```

Due esiti, e la differenza conta:

* **200 OK** — il lavoro è effettivamente terminato durante la richiesta. `completed` è `true` e `dataset` è la riga finale. È ciò che fa un ri-download di un dataset pubblico.
* **202 Accepted** — l'aggiornamento è stato *avviato*. La riga è bloccata e contrassegnata come `PROCESSING`, e un connettore viene eseguito pochi secondi dopo. `completed` è `false`. Effettua polling sul dataset, oppure ascolta sul websocket, finché `processingStatus` non esce da `PROCESSING`.

Un `202` non è un fallimento e non è un successo — è una promessa. Tratta "la chiamata è tornata" e "i dati sono aggiornati" come due fatti distinti, e verifica il secondo prima di dire a qualcuno che la dashboard è aggiornata. La chiamata che lo verifica è [`get_data_freshness`](#how-old-are-the-numbers) — tutto ciò che precede quella sezione riguarda il far muovere i dati, non quanto siano vecchi.

## Caricamenti incrementali, e l'esecuzione che non è ancora incrementale[​](#caricamenti-incrementali-e-lesecuzione-che-non-è-ancora-incrementale "Link diretto a Caricamenti incrementali, e l'esecuzione che non è ancora incrementale")

Passa una modalità per prelevare solo le righe nuove:

```
from vibedasher.models import RefreshRequest



result = client.datasets.refresh(dataset_id, RefreshRequest(mode="incremental"))

print(result.requested_mode, "->", result.effective_mode)
```

`requestedMode` ripete ciò che hai richiesto. **`effectiveMode` è ciò che è effettivamente stato eseguito, e non sono sempre uguali.** Una richiesta incrementale contro un dataset senza ancora un checkpoint riporta `effectiveMode: "bootstrap"`: viene eseguita come una sovrascrittura completa *e* registra il watermark, quindi l'esecuzione **successiva** è la prima genuinamente incrementale.

Ramifica in base a `effectiveMode`, mai in base a ciò che hai richiesto. (Questa è la stessa regola di `client.query`, che riporta la corsia di esecuzione effettivamente usata piuttosto che quella richiesta — uno schema da interiorizzare: la piattaforma ti dice cosa è successo, ed è compito tuo leggerlo.)

Per forzare la prossima esecuzione a recuperare di nuovo tutto, azzera il watermark:

```
client.datasets.reset_checkpoint(dataset_id)
```

## Aggiornamento pianificato[​](#aggiornamento-pianificato "Link diretto a Aggiornamento pianificato")

Una pianificazione è una riga che crei una volta. Porta con sé un'espressione cron e il progetto dbt che aggiorna:

```
from vibedasher.models import RefreshCreate



schedule = client.refreshes.create_refresh(

    RefreshCreate(name="nightly", project_id=project_id, cron="0 3 * * *")

)

print(schedule.refresh.next_run, schedule.schedule_registration.registered)
```

La risposta porta un blocco `scheduleRegistration`. **Controlla `registered`.** È `true` solo quando la pianificazione è realmente attiva; quando è `false`, `reason` e `message` dicono perché, e `nextRun` è null.

Quel campo esiste a causa di un'interruzione reale, e la storia è il miglior argomento a favore del design attuale. L'implementazione originale registrava ogni pianificazione come una propria regola cloud, avvolgeva la chiamata in un `except` generico, e rispondeva `201 Created` con un sicuro `nextRun`. Il ruolo API non aveva il permesso di creare quelle regole, quindi **non veniva mai registrato nulla** — e a ogni cliente veniva detto che i suoi dati si sarebbero aggiornati. È stato scoperto il 2026‑09‑05: non esisteva alcuna regola, e l'unica pianificazione nel database aveva un `nextRun` di cinque mesi nel passato e un conteggio di esecuzioni pari a zero.

La correzione non è stata una migliore gestione degli errori. È stata la rimozione della cosa che poteva fallire: **la riga *è* la pianificazione.** Un poller cron viene eseguito ogni cinque minuti, rivendica ogni riga scaduta con un compare-and-set, e la esegue. Non c'è alcuna registrazione esterna che possa riuscire o fallire, quindi l'unica cosa che può andare storta al momento della creazione è un'espressione cron che non può essere valutata — e ora viene rifiutata subito, non al momento dell'esecuzione.

Due conseguenze da tenere in conto:

* Una pianificazione scatta **entro circa cinque minuti** dal suo orario previsto, non al secondo esatto. Cron qui significa "non prima di", non "esattamente alle".
* `scheduleRegistration.backend` riporta `db_cron`. Viene mantenuto nella risposta perché i client si ramificano in base ad esso.

## Leggere cosa è successo[​](#leggere-cosa-è-successo "Link diretto a Leggere cosa è successo")

Ogni riga di pianificazione porta la propria cronologia — `lastRun`, `lastStatus`, `lastError`, `executionCount`, `nextRun`:

```
for r in client.refreshes.iter_all():

    print(r.name, r.last_status, r.last_run, r.execution_count, r.last_error)
```

`executionCount` è il campo da tenere d'occhio. Una pianificazione esistente da una settimana con `executionCount: 0` non è lenta — non sta girando, ed è esattamente il fallimento che il design sopra è stato costruito per rendere visibile invece che silenzioso.

## Quanto sono vecchi questi numeri?[​](#how-old-are-the-numbers "Link diretto a Quanto sono vecchi questi numeri?")

Tutto quanto precede risponde a *«la pipeline funziona?»*. Non risponde a *«ciò che questa dashboard mostra è aggiornato?»*, e le due cose divergono in entrambe le direzioni:

* Una pianificazione può girare perfettamente e non caricare nulla di nuovo. `executionCount` sale, `lastStatus` è `success`, e la riga più recente è ancora di marzo.
* **Un dataset può non avere alcuna pianificazione**, e allora `executionCount` non ha alcuna riga da leggere. Un caricamento statico è il caso comune: le sue righe cambiano quando qualcuno ne carica di nuove e in nessun altro momento, così un elenco di pianificazioni sano può convivere con un dataset vecchio di nove giorni senza dirne nulla. L'assenza di una pianificazione non è una pianificazione con un contatore sbagliato.

Una sola chiamata risponde alla vera domanda del lettore, misurata sulle righe e non sulla contabilità interna della piattaforma:

```
f = client.datasets.get_freshness(dataset_id).freshness

print(f.as_of, f.age_days, f.status)
```

```
const { freshness } = await client.datasets.getFreshness(datasetId);

console.log(freshness.asOf, freshness.ageDays, freshness.status);
```

Via MCP è `get_data_freshness`, che accetta **esattamente uno** tra `viz_id` e `dataset_id`. Con un `viz_id` risolve ogni dataset che la dashboard legge e riporta **il più vecchio** — una dashboard è aggiornata solo quanto il suo input più vecchio. La rotta REST è `GET /v1/datasets/{dataset_id}/freshness`.

### I tre campi, e perché sono tre[​](#i-tre-campi-e-perché-sono-tre "Link diretto a I tre campi, e perché sono tre")

| Campo          | Che cos'è                                                                                                                                                                                                   |
| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `asOf`         | Il valore più recente nella **colonna temporale dei dati stessi**. Questa è la risposta, e non può lusingarti: è letta dalle righe. `ageDays` è quanto è vecchio; negativo significa dati post-datati.      |
| `lastLoadedAt` | Quando le righe sono **arrivate** l'ultima volta. Un fatto di pipeline, non un'età — un file caricato stamattina può non contenere nulla più recente di marzo. Non citarlo mai come data di riferimento.    |
| `refresh`      | Se qualcosa è pianificato per rendere i numeri più nuovi, **inclusa la risposta «niente, e niente può esserlo»** per un file piatto. È il campo che copre il caso «nessuna pianificazione» descritto sopra. |

`status` dice quanto fidarsi di `asOf`: `MEASURED` (è reale), `NO_TIME_COLUMN` (i dati non portano alcuna colonna di data, quindi non hanno una data di riferimento da avere), oppure `UNKNOWN` — che significa che il fatto non è stato stabilito e **non è mai** un via libera.

`lastLoadedSource` nomina quale orologio ha risposto a `lastLoadedAt`. `iceberg_snapshot` è l'ora di scrittura fisica dello snapshot corrente della tabella — il solo vero istante di arrivo, e uno che una rinomina non può spostare. **`not_established` significa che `lastLoadedAt` è `null` di proposito**: quel percorso di lettura non ha consultato lo snapshot, quindi invece di rispondere con l'`updatedAt` della riga — un orologio di scrittura che qualsiasi modifica fa avanzare, e che una volta ha riportato righe arrivate nove giorni dopo il fatto — il campo rinuncia. Un campo che rinuncia è strettamente meglio di uno che risponde con l'orologio sbagliato. La rotta sopra è la chiamata che legge quello giusto. `get_viz` e `list_vizzes` portano un `dataFreshness` gratuito, definito sulla postura di aggiornamento e `UNKNOWN` sulla data di riferimento; questa chiamata è ciò che risolve quell'`UNKNOWN`.

### Ciò che *non* è l'età dei dati[​](#ciò-che-non-è-letà-dei-dati "Link diretto a ciò-che-non-è-letà-dei-dati")

Né `publishStatus`, né `lastBuild.createdAt`, né `processingStatus`, né `updatedAt` rispondono a questa domanda. Sono, nell'ordine: se esiste un bundle, quando il **codice** è stato compilato, se il file è stato analizzato una volta, e quando la **riga** è cambiata l'ultima volta. Ognuno di essi si legge come *aggiornato* su una dashboard i cui numeri hanno settimane. Se stai per riportare l'attualità da uno di questi, chiama invece la rotta di freschezza.

Costa un `SELECT MAX(<colonna di data>)` per dataset — la stessa sonda che la dashboard renderizzata esegue già al montaggio.

## Dove vive ogni gesto[​](#dove-vive-ogni-gesto "Link diretto a Dove vive ogni gesto")

* **Le pianificazioni sono la superficie MCP.** `create_refresh`, `list_refreshes`, `get_refresh` e `delete_refresh` coprono tutto il ciclo di vita di un aggiornamento ricorrente dal tuo agente.
* **«Aggiorna ora» è l'SDK e la REST API.** Un'esecuzione una tantum è una sola chiamata lì — vedi il **[Riferimento API](/it/reference/vibedasher-sdk-api.md)**.
* I **progetti dbt** portano le proprie rotte di esecuzione e aggiornamento (`/v1/dbt/projects/{project_id}/run`, `/v1/dbt/projects/{project_id}/refreshes`).
