Pular para o conteúdo principal

Mantenha seus dados atualizados

Um painel só vale sua URL se os números nele estiverem atuais. Essa é a parte que uma sessão de agente não pode deixar para trás: seu agente constrói o painel e vai embora, e os dados por baixo precisam continuar chegando em um agendamento, por meses, sem que ninguém precise acompanhar.

A atualidade dos dados é, portanto, estado da plataforma, não código do painel. Você a configura uma vez contra um conjunto de dados; ela continua rodando quer alguém esteja com uma sessão aberta, quer não.

As três formas de os dados avançarem​

O que éQuem dispara
Atualização sob demandaRebuscar um conjunto de dados agoraVocê, seu agente, ou seu CI — POST /v1/datasets/{dataset_id}/refresh
Carga incrementalBuscar apenas o que mudou desde a última execuçãoA mesma chamada, com mode: "incremental"
Atualização agendadaUma expressão cron que a plataforma executa para vocêNinguém — o poller de cron da plataforma

Atualize um conjunto de dados agora​

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);

Dois resultados possíveis, e a diferença importa:

  • 200 OK — o trabalho realmente terminou dentro da requisição. completed é true e dataset é a linha final. É isso que um novo download de um conjunto de dados público faz.
  • 202 Accepted — a atualização foi iniciada. A linha é travada e marcada como PROCESSING, e um conector roda segundos depois. completed é false. Faça polling do conjunto de dados, ou escute no websocket, até que processingStatus saia de PROCESSING.

Um 202 não é uma falha e não é um sucesso — é uma promessa. Trate "a chamada retornou" e "os dados estão atuais" como dois fatos diferentes, e verifique o segundo antes de dizer a alguém que o painel está em dia. A chamada que verifica isso é get_data_freshness — tudo o que vem antes dessa seção trata de fazer os dados se moverem, não de quão antigos eles são.

Cargas incrementais, e a execução que ainda não é incremental​

Passe um modo para buscar apenas linhas novas:

from vibedasher.models import RefreshRequest

result = client.datasets.refresh(dataset_id, RefreshRequest(mode="incremental"))
print(result.requested_mode, "->", result.effective_mode)

requestedMode ecoa o que você pediu. effectiveMode é o que de fato rodou, e eles nem sempre são iguais. Uma requisição incremental contra um conjunto de dados que ainda não tem checkpoint retorna effectiveMode: "bootstrap": ela roda como uma sobrescrita completa e registra a marca d'água, então a próxima execução é a primeira genuinamente incremental.

Baseie sua lógica em effectiveMode, nunca no que você pediu. (Essa é a mesma regra de client.query, que reporta a via de execução que de fato usou, em vez daquela que você pediu — um padrão que vale a pena internalizar: a plataforma diz o que aconteceu, e é seu trabalho ler isso.)

Para forçar a próxima execução a buscar tudo de novo, limpe a marca d'água:

client.datasets.reset_checkpoint(dataset_id)

Atualização agendada​

Um agendamento é uma linha que você cria uma vez. Ela carrega uma expressão cron e o projeto dbt que atualiza:

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)

A resposta carrega um bloco scheduleRegistration. Verifique registered. Ele é true somente quando o agendamento está de fato ativo; quando é false, reason e message dizem o porquê, e nextRun é nulo.

Esse campo existe por causa de uma interrupção real, e a história é o melhor argumento a favor do design atual. A implementação original registrava cada agendamento como sua própria regra na nuvem, envolvia a chamada em um except amplo, e respondia 201 Created com um nextRun confiante. O papel da API não tinha permissão para criar essas regras, então nada jamais foi registrado — e todo cliente era informado de que seus dados seriam atualizados. Isso foi descoberto em 05-09-2026: zero regras existiam, e o único agendamento no banco de dados tinha um nextRun cinco meses no passado e uma contagem de execuções igual a zero.

A correção não foi um tratamento de erros melhor. Foi remover a coisa que podia falhar: a linha é o agendamento. Um poller de cron roda a cada cinco minutos, reivindica cada linha vencida com um compare-and-set, e a executa. Não há registro externo para ter sucesso ou falhar, então a única coisa que pode dar errado no momento da criação é uma expressão cron que não pode ser avaliada — e isso agora é rejeitado logo de início, não no momento do disparo.

Duas consequências que vale a pena planejar:

  • Um agendamento dispara dentro de cerca de cinco minutos do seu horário previsto, não no segundo exato. Cron aqui significa "não antes de", não "exatamente às".
  • scheduleRegistration.backend traz db_cron. Ele é mantido na resposta porque clientes baseiam lógica nele.

Lendo o que aconteceu​

Cada linha de agendamento carrega seu próprio histórico — 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 é o campo a observar. Um agendamento que existe há uma semana com executionCount: 0 não está lento — ele não está rodando, e é exatamente essa a falha que o design acima foi construído para tornar visível em vez de silenciosa.

Qual é a idade destes números?​

Tudo acima responde a «o pipeline está funcionando?». Não responde a «o que este painel mostra está atual?», e as duas coisas se separam nas duas direções:

  • Um agendamento pode rodar perfeitamente e não carregar nada de novo. executionCount sobe, lastStatus é success, e a linha mais recente ainda é de março.
  • Um conjunto de dados pode não ter agendamento algum, e então executionCount não tem linha para ler. Um upload estático é o caso comum: suas linhas mudam quando alguém sobe novas e em nenhum outro momento, então uma lista de agendamentos saudável pode conviver com um conjunto de dados de nove dias sem dizer nada sobre ele. A ausência de um agendamento não é um agendamento com contador ruim.

Uma única chamada responde à pergunta real do leitor, medida a partir das linhas e não da contabilidade da própria plataforma:

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);

No MCP é get_data_freshness, que aceita exatamente um de viz_id ou dataset_id. Com um viz_id, ele resolve cada conjunto de dados que o painel lê e reporta o mais antigo — um painel só está tão atual quanto sua entrada mais velha. A rota REST é GET /v1/datasets/{dataset_id}/freshness.

Os três campos, e por que são três​

CampoO que é
asOfO valor mais recente na própria coluna temporal dos dados. Esta é a resposta, e ela não pode lisonjear: é lida das linhas. ageDays é a idade disso; negativo significa dados com data futura.
lastLoadedAtQuando as linhas chegaram pela última vez. Um fato de pipeline, não uma idade — um arquivo carregado hoje de manhã pode não conter nada mais recente que março. Nunca cite isso como data de referência.
refreshSe há algo agendado para deixar os números mais novos, incluindo a resposta «nada, e nada pode haver» para um arquivo plano. É o campo que cobre o caso «sem agendamento» descrito acima.

status diz quanto confiar em asOf: MEASURED (é real), NO_TIME_COLUMN (os dados não têm coluna de data, portanto não têm data de referência a ter), ou UNKNOWN — que significa que o fato não foi estabelecido e nunca é uma aprovação.

lastLoadedSource nomeia qual relógio respondeu a lastLoadedAt. iceberg_snapshot é o horário físico de escrita do snapshot atual da tabela — o único instante de chegada verdadeiro, e um que uma renomeação não pode mover. not_established significa que lastLoadedAt é null de propósito: aquele caminho de leitura não consultou o snapshot, então em vez de responder com o updatedAt da linha — um relógio de escrita que qualquer edição adianta, e que uma vez reportou linhas chegando nove dias depois do que chegaram — o campo recusa. Um campo que recusa é estritamente melhor que um que responde com o relógio errado. A rota acima é a chamada que lê o certo. get_viz e list_vizzes carregam um dataFreshness gratuito, definido sobre a postura de atualização e UNKNOWN sobre a data de referência; esta chamada é o que resolve esse UNKNOWN.

O que não é a idade dos dados​

Nem publishStatus, nem lastBuild.createdAt, nem processingStatus, nem updatedAt respondem a esta pergunta. Eles são, em ordem: se um bundle existe, quando o código foi compilado, se o arquivo foi analisado uma vez, e quando a linha mudou pela última vez. Cada um deles se lê como atual em um painel cujos números têm semanas. Se você está a ponto de reportar atualidade a partir de qualquer um deles, chame a rota de frescor em vez disso.

Custa um SELECT MAX(<coluna de data>) por conjunto de dados — a mesma sondagem que o painel renderizado já executa ao montar.

Onde vive cada gesto​

  • As programações são a superfície MCP. create_refresh, list_refreshes, get_refresh e delete_refresh cobrem todo o ciclo de vida de uma atualização recorrente a partir do seu agente.
  • "Atualize agora" é o SDK e a API REST. Uma execução avulsa é uma única chamada lá — veja a Referência da API.
  • Projetos dbt carregam suas próprias rotas de execução e atualização (/v1/dbt/projects/{project_id}/run, /v1/dbt/projects/{project_id}/refreshes).