Skip to content

Schedule

Um agendamento executa flows de forma recorrente. Ele vive em schedules/<slug>--<id>.yaml.

bash
lumo new schedule --name "Carga Diária"

Ligue o autocomplete no editor colando esta linha no topo do arquivo:

yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/schedule.schema.json

Antes de agendar: todos os flows precisam estar publicados num dw-desk, e todos precisam estar no mesmo agente. O agendamento herda o agente dos flows e não tem campo de agente próprio.

Modelo de configuração

yaml
# ── header ────────────────────────────────────────────────
id: integer                # obrigatório, >= 1
kind: schedule             # obrigatório, literal "schedule"
lumo: v2                   # obrigatório, literal "v2"
tenantId: integer          # obrigatório, >= 1
---
# ── body ──────────────────────────────────────────────────
nome: string               # obrigatório, mínimo 1 caractere
ativo: boolean             # obrigatório
timezone: string           # obrigatório, nome IANA (ex: America/Sao_Paulo)
tags: [string]

flows:                     # obrigatório. executados em sequência
  - flow_etl_id: integer   # obrigatório
    context: string        # obrigatório: Total | Incremental | Temporal | Temporal:Days:<n>

triggers:                  # obrigatório. quando o agendamento dispara
  # Hour / Minute: a cada N unidades, dentro de uma janela diária
  - type: Hour             # Hour | Minute. Minute exige schedulingGranularity=minutes no tenant
    period: integer        # obrigatório, >= 1
    start_when: string     # obrigatório, ISO 8601 UTC (ex: 2026-01-01T07:00:00.000Z)
    start_time: "HH:MM"    # obrigatório, início da janela
    end_time: "HH:MM"      # obrigatório, fim da janela

  # Day: uma vez por dia
  - type: Day
    start_when: "HH:MM"    # obrigatório, hora do dia

  # Week: num dia da semana
  - type: Week
    period: integer        # obrigatório, 0=domingo até 6=sábado
    start_when: "HH:MM"    # obrigatório

  # Month: num dia do mês
  - type: Month
    period: integer        # obrigatório, 1 a 31 (dia do mês, não intervalo)
    start_when: "HH:MM"    # obrigatório

  # Mudança de dados (CDC): sonda a origem em vez de esperar um horário
  - type: CDC
    period: integer        # obrigatório, EM SEGUNDOS para este tipo, 5 a 3600
                           # (nos outros tipos acima, period é hora/minuto/dia; aqui não)
    start_when: string     # obrigatório, ISO 8601 completo (não aceita "HH:MM" aqui)
    start_time: "HH:MM"    # opcional, início da janela em que a sondagem roda
    end_time: "HH:MM"      # opcional, fim da janela
    config:                # obrigatório
      kind: query-scalar   # único valor aceito hoje
      credential: string   # chave da credencial da sonda; pode ser outra além das dos flows
      sql: string           # SELECT ou WITH, instrução única, até 8 KB, 1 linha x 1 coluna
      timeout_seconds: integer  # opcional, default 5, precisa ser menor que period

parallel_lane: boolean     # opcional, default false. Roda fora do limite de execuções
                           # simultâneas do agente. Vale para qualquer tipo de gatilho acima.

start_when muda de formato conforme o type. Em Hour e Minute, é um timestamp ISO completo em UTC. Em Day, Week e Month, é apenas a hora do dia, no formato HH:MM. Em CDC, volta a ser um timestamp ISO completo, mas por um motivo diferente (o agente não usa este campo para decidir quando sondar); veja a especificação de start_when mais abaixo.

Em Month, period é o dia do mês, e não um intervalo de meses. O dia 31 é ajustado para o último dia válido nos meses mais curtos.

Em CDC, period está em segundos, não em horas, minutos ou dias. É o único tipo em que a unidade muda para uma escala menor; confira sempre o type antes de reaproveitar um valor de period de outro gatilho.

Configuração completa

yaml
# schedules/carga-diaria--2478.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/schedule.schema.json
id: 2478
kind: schedule
lumo: v2
tenantId: 853
---
nome: Carga Diária
ativo: true

# Nome IANA, obrigatório. Sem ele, o horário do gatilho fica ambíguo entre
# UTC e local.
timezone: America/Sao_Paulo

# Executados em sequência: o segundo começa quando o primeiro termina.
# Carregue as dimensões antes dos fatos.
flows:
  - flow_etl_id: 44547
    context: Total          # dimensão pequena: apaga tudo e recarrega

  - flow_etl_id: 44531
    # Recarrega os últimos 31 dias. A tabela precisa ser key_type: unique,
    # senão a janela recarregada duplica as linhas a cada execução.
    context: Temporal:Days:31

triggers:
  # A cada 2 horas, entre 07:00 e 19:00, no fuso acima.
  - type: Hour
    period: 2
    start_when: "2026-01-01T07:00:00.000Z"
    start_time: "07:00"
    end_time: "19:00"

tags: [producao]

Especificação: header

id

Tipo: integer (>= 1) · Obrigatório: sim

kind

Tipo: string · Obrigatório: sim · Valor: schedule

lumo

Tipo: string · Obrigatório: sim · Valor: v2

tenantId

Tipo: integer (>= 1) · Obrigatório: sim

Especificação: body

nome

Tipo: string (mínimo 1 caractere) · Obrigatório: sim

Nome do agendamento. Descreva o que ele faz e como carrega, por exemplo Vendas Diário Temporal 7d.

ativo

Tipo: boolean · Obrigatório: sim

false desliga o agendamento sem apagá-lo. Equivale a lumo schedule pause.

timezone

Tipo: string (mínimo 1 caractere) · Obrigatório: sim

Nome de fuso IANA, como America/Sao_Paulo. Define quando os gatilhos disparam. É obrigatório para não deixar o horário ambíguo entre UTC e local.

flows

Tipo: array de objetos · Obrigatório: sim

Flows executados, em sequência. Cada item exige flow_etl_id e context. Veja Especificação: flow do agendamento.

triggers

Tipo: array de objetos · Obrigatório: sim

Quando o agendamento dispara. Veja Especificação: trigger.

parallel_lane

Tipo: boolean · Obrigatório: não · Default: false

Roda este agendamento fora do limite de execuções simultâneas do agente, numa pista própria. Vale para o agendamento inteiro, qualquer que seja o tipo de gatilho (inclusive CDC), não só as sondagens. Use só em agendamentos leves, para que uma carga pesada de outro agendamento não segure este atrás dela na fila.

O teto da pista paralela (parallel_lane_slots) é configurado por agente, não neste arquivo. Um teto igual a 0 desliga a pista, não significa "sem limite".

tags

Tipo: array de string · Obrigatório: não · Default: []

Especificação: flow do agendamento

Cada item de flows.

flow_etl_id

Tipo: integer (>= 1) · Obrigatório: sim

Id do flow. O flow precisa estar publicado num dw-desk.

context

Tipo: string · Obrigatório: sim

Como o flow carrega nesta execução.

ValorComportamento
TotalApaga tudo e recarrega tudo.
IncrementalAcrescenta ou atualiza, sem apagar.
TemporalRecarrega a janela padrão, expandida pelo servidor com o temporal_value do flow.
TemporalFutureIgual ao Temporal, mas para a frente. Também expandida pelo servidor.
Temporal:Days:<n>Recarrega os últimos <n> dias.
Temporal:Future:<n>Recarrega os próximos <n> dias.
Temporal:Month:<M>-<AAAA>Recarrega um mês do calendário, por exemplo Temporal:Month:3-2026.
Temporal:Year:<AAAA>Recarrega um ano do calendário. Temporal:Year:Current resolve para o ano corrente.
KeySweepNão é uma carga. Roda a consulta de chaves do flow e marca como excluído, na tabela de destino, tudo que a origem não devolveu.
yaml
context: Temporal:Days:31

Temporal e TemporalFuture são as únicas formas curtas. Month e Year não têm forma curta e precisam ser escritas expandidas.

O context escolhe a janela de extração; o load_type do flow decide o predicado de exclusão. Os dois precisam concordar, e o lumo lint cobra isso.

context: KeySweep

KeySweep é o contexto da sincronização de exclusões. Ele não carrega dado: o agente roda apenas a consulta de key_sweep do flow, lê da origem só as chaves ainda existentes e marca como excluído, no destino, tudo que não veio.

Exige que o flow tenha load_type: Incremental e key_sweep configurado. Sem os dois, o servidor recusa no push.

yaml
flows:
  # A carga do dado, de hora em hora.
  - flow_etl_id: 44531
    context: Incremental

  # Num agendamento SEPARADO, uma vez por noite: só a varredura.
  - flow_etl_id: 44531
    context: KeySweep

WARNING

A consulta de chaves precisa devolver todas as chaves vivas na origem, sem filtro de período. Tudo que não vier é marcado como excluído, sem erro nenhum. Uma consulta janelada apaga o histórico anterior à janela, e isso não se recupera sozinho. Veja Sincronização de Exclusões.

Exige agente 1.27.0 ou mais novo.

Uma janela Temporal recarregada sobre uma tabela key_type: duplicate duplica as linhas a cada execução. Use unique.

Especificação: trigger

Cada item de triggers. As chaves exigidas mudam conforme type.

type

Tipo: string · Obrigatório: sim · Valores: Hour, Minute, Day, Week, Month, CDC

ValorDisparaExige
HourA cada period horas, dentro da janela.period, start_when, start_time, end_time
MinuteA cada period minutos, dentro da janela.period, start_when, start_time, end_time
DayUma vez por dia.start_when
WeekNum dia da semana.period, start_when
MonthNum dia do mês.period, start_when
CDCA cada period segundos, roda a consulta de config.sql e dispara quando o valor devolvido é diferente do anterior (não é "maior que": um valor que diminui também dispara). Rótulo na tela: Mudança de dados (CDC).period, start_when, config

Minute só funciona em tenant com schedulingGranularity igual a minutes.

CDC é para origem sem CDC nativo acessível (Oracle, ODBC genérico, Firebird, Informix, IRIS): em vez de rodar o flow por horário e descobrir se algo mudou pela extração completa, uma sonda leve responde essa pergunta antes. É o mesmo mecanismo do sql_trigger_value do Looker. Ele não detecta linha excluída (ela permanece no data warehouse até uma carga Total) e não informa ao flow quais registros mudaram: ele só dispara, e o flow carrega do jeito que já carrega hoje.

period

Tipo: integer · Obrigatório: em Hour, Minute, Week, Month e CDC

O significado muda conforme o type. Confira sempre o type antes de copiar um valor de period de um gatilho para outro, porque a unidade não é a mesma.

typeSignificado de periodFaixa
HourIntervalo em horas.>= 1
MinuteIntervalo em minutos.>= 1
WeekDia da semana, com 0 igual a domingo.0 a 6
MonthDia do mês.1 a 31
CDCIntervalo em segundos entre uma sondagem e outra. Único tipo em que period não é hora, minuto nem dia.5 a 3600

Em CDC, period: 5 (o mínimo da faixa) só passa no lumo lint; o servidor recusa no push. timeout_seconds precisa ser menor que period, e o default de timeout_seconds é 5. Com o default, o menor period que funciona é 6. Para usar period: 5, baixe timeout_seconds para menos de 5.

start_when

Tipo: string · Obrigatório: sim

Em Hour e Minute, é um timestamp ISO 8601 em UTC, como 2026-01-01T07:00:00.000Z, que marca quando o agendamento passa a valer.

Em Day, Week e Month, é apenas a hora do dia, no formato HH:MM.

Em CDC, é de novo um timestamp ISO 8601 completo (não aceita HH:MM aqui). Detalhe que vale saber: o agente não usa este campo para decidir quando sondar, apenas quem limita isso é start_time/end_time. start_when é exigido em CDC só por consistência com os demais tipos de gatilho.

start_time

Tipo: string HH:MM · Obrigatório: em Hour e Minute · Opcional: em CDC

Início da janela diária em que o gatilho pode disparar. Em CDC, restringe quando a sondagem roda; fora da janela, o detector fica parado. Sem start_time/end_time, a sondagem roda o dia inteiro.

end_time

Tipo: string HH:MM · Obrigatório: em Hour e Minute · Opcional: em CDC

Fim da janela diária.

config

Tipo: object · Obrigatório: em CDC

Configuração da sonda. Só existe no tipo CDC; os demais tipos de gatilho não têm este campo. Veja Especificação: config da sonda CDC.

Especificação: config da sonda CDC

Campos de config, exigido pelo trigger type: CDC.

kind

Tipo: string · Obrigatório: sim · Valores: query-scalar

Modo de detecção. Hoje só existe query-scalar: compara o valor devolvido pela consulta de sql com o da sondagem anterior. O campo existe para modos futuros (por chave, por log lógico do banco) entrarem sem precisar de um type de gatilho novo.

O gatilho decide quando rodar e não passa ao flow a lista de registros alterados; o que trazer continua sendo do flow, pelo tipo de carga. É decisão de desenho, não pendência: veja por que o gatilho não passa a lista de registros alterados.

credential

Tipo: string (mínimo 1 caractere) · Obrigatório: sim

Chave da credencial que a sonda usa para conectar (mesmo picker do editor de flow; descubra com lumo list credential). Pode ser diferente das credenciais usadas pelos flows do agendamento, o que é útil para dar à sonda um usuário próprio.

Use uma credencial somente leitura para a sonda. A checagem de "só SELECT/WITH" descrita em sql é textual, feita sobre o texto da consulta; ela não é uma garantia de segurança. Existem formas de escrever que não usam nenhuma palavra reconhecível como escrita (nextval, setval, lo_import e funções do usuário com efeito colateral são exemplos que passam pela checagem). A defesa real contra isso é a credencial somente leitura, não a validação textual.

sql

Tipo: string (máximo 8192 bytes) · Obrigatório: sim

Consulta de sondagem. Precisa ser:

  • instrução única (sem ; fora de um literal de texto);
  • iniciada em SELECT ou WITH (comentários antes não atrapalham);
  • capaz de devolver exatamente uma linha, com 1 a 8 colunas.

As duas primeiras regras são validadas ao salvar. A terceira só é possível checar em tempo de execução: se a sondagem devolver mais de uma linha, nenhuma coluna ou mais de oito, ela não dispara, não avança e é reportada como erro de configuração, com mensagem distinta de erro de conexão.

O sistema recusa, pelo texto, palavras de escrita óbvias (INSERT, UPDATE, DELETE, DROP e afins). Essa recusa é textual, não é fronteira de segurança; veja a recomendação de credencial somente leitura em credential acima.

yaml
sql: SELECT MAX(UPDATED_AT) FROM PEDIDOS

Mais de uma coluna

Com duas ou mais colunas, a sonda dispara quando qualquer uma delas muda. Isso existe para uma consulta em particular:

yaml
sql: SELECT COUNT(*), MAX(UPDATED_AT) FROM PEDIDOS

É a única forma de ver os três casos na mesma sonda. A contagem muda quando uma linha entra ou sai, e o maior valor da coluna muda quando uma linha é alterada; cada uma sozinha é cega para o que a outra vê.

São duas colunas em vez de um valor concatenado porque concatenar amarra a consulta a um dialeto: || no Oracle e no PostgreSQL, + no SQL Server, CONCAT no MySQL. Numa conexão ODBC o banco de destino sequer é conhecido pelo Lumo, então não haveria dialeto a escolher.

O valor guardado passa a ser a combinação das colunas. Na tela ele aparece com os valores separados por vírgula; internamente é uma string só, e a comparação continua sendo por diferença.

Versão do agente

Sonda de mais de uma coluna exige o agente 1.26.0 ou mais novo. Contra um agente anterior a sondagem falha como erro de configuração, e o agendamento não dispara.

timeout_seconds

Tipo: integer · Obrigatório: não · Default: 5

Precisa ser menor que period, para uma sondagem não encavalar na seguinte. Uma sondagem que estoura o timeout não dispara e não avança; conta como erro e tenta de novo no próximo ciclo.

Exemplo: gatilho CDC

yaml
# schedules/cdc-pedidos--2481.yaml
# yaml-language-server: $schema=https://docs.horusbi.com.br/schemas/v2/schedule.schema.json
id: 2481
kind: schedule
lumo: v2
tenantId: 853
---
nome: Pedidos CDC
ativo: true
timezone: America/Sao_Paulo

flows:
  - flow_etl_id: 44560
    context: Incremental

triggers:
  # Sonda a cada 10 segundos, só entre 07:00 e 22:00. Dispara quando o
  # valor devolvido pela consulta mudar.
  - type: CDC
    period: 10
    start_when: "2026-01-01T00:00:00.000Z"
    start_time: "07:00"
    end_time: "22:00"
    config:
      kind: query-scalar
      credential: oracle-hosp        # pode ser diferente da credencial usada pelos flows
      sql: SELECT MAX(UPDATED_AT) FROM PEDIDOS
      timeout_seconds: 5

# Fora do limite de execuções simultâneas do agente: útil porque a sonda
# em si é barata, mesmo que o dataflow disparado não seja.
parallel_lane: true

tags: [producao]

Campos gerenciados pelo servidor

CampoO que é
versionVersão do recurso.
criado_em, criado_por, publicado_em, publicado_porAuditoria.