Fila de trabalho
Tarefas com prioridade, retentativa por tarefa e relatório.
Este é o código completo do exercício 174_projeto_worker.df, que roda e verifica a si mesmo.
174_projeto_worker.df
adopt Arcane.Collections as Col
adopt Arcane.Time as Time
adopt Arcane.Logging as Log
adopt Arcane.Text as Text
// ═══ MODELO ═══
enum Estado:
Pendente
Executando
Concluida
Falhou
record Tarefa:
id: Integer
nome: String
prioridade: Integer
duracao_ms: Integer
// ═══ FILA COM PRIORIDADE ═══
fila := Col.priority_queue()
tarefas := [
Tarefa(1, "enviar email", 5, 10),
Tarefa(2, "processar pagamento", 1, 20),
Tarefa(3, "gerar relatorio", 8, 15),
Tarefa(4, "backup", 9, 25),
Tarefa(5, "alerta de seguranca", 1, 5),
Tarefa(6, "limpar cache", 7, 10)
]
cycle t in tarefas:
fila.push(t, t.prioridade)
out Text.box("Fila de Trabalho")
out $"tarefas na fila: {fila.size()}"
assert fila.size() is 6, "seis tarefas"
// ═══ EXECUCAO ═══
registro := Log.logger("worker", "INFO")
registro.colored(no)
historico := []
falhas := {"n": 0}
action executar(t):
// Simula o trabalho levando o tempo declarado
wait t.duracao_ms
given t.nome.contains("pagamento"):
trigger "gateway indisponivel"
yield $"{t.nome} concluida"
out ""
out "── processando por prioridade ──"
crono := Time.stopwatch()
crono.start()
persist yes:
t := fila.pop()
given t is void:
halt
resultado := {"tarefa": t, "estado": Estado.Executando, "detalhe": ""}
retry 2:
saida := executar(t)
resultado["estado"] := Estado.Concluida
resultado["detalhe"] := saida
handle e:
resultado["estado"] := Estado.Falhou
resultado["detalhe"] := e
falhas["n"] := falhas["n"] + 1
historico.append(resultado)
marca := "ok " given resultado["estado"] is Estado.Concluida otherwise "ERR"
out $" [p{t.prioridade}] {marca} {t.nome.pad_end(22)} {resultado["detalhe"]}"
decorrido := crono.stop()
// ═══ RELATORIO ═══
out ""
out Text.box("Relatorio")
concluidas := historico >> sift h: h["estado"] is Estado.Concluida
falhadas := historico >> sift h: h["estado"] is Estado.Falhou
out $"total: {len(historico)}"
out $"concluidas: {len(concluidas)}"
out $"falhadas: {len(falhadas)}"
out $"tempo: {round(decorrido * 1000, 1)} ms"
assert len(historico) is 6, "todas processadas"
assert len(falhadas) is 1, "so o pagamento falhou"
assert falhas["n"] is 1, "uma falha contabilizada"
// A ordem seguiu a prioridade, nao a de insercao
ordem := historico >> morph h: h["tarefa"].prioridade
out ""
out $"ordem de execucao (prioridade): {ordem}"
assert ordem is sorted(ordem), "menor prioridade primeiro"
assert ordem[0] is 1, "as urgentes vieram antes"
// Agrupar por estado
out ""
out "── por estado ──"
cycle nome_estado in Estado.names():
quantos := len(historico >> sift h: h["estado"].name is nome_estado)
given quantos bigger 0:
out $" {nome_estado.pad_end(12)} {"#".repeat(quantos)} ({quantos})"
// As que falharam voltam para a fila
out ""
given len(falhadas) bigger 0:
out "── reenfileirando falhas ──"
cycle h in falhadas:
t := h["tarefa"]
fila.push(t, 0)
out $" {t.nome} volta com prioridade maxima"
assert fila.size() is 1, "uma tarefa reenfileirada"Fila de prioridade#
Menor número = mais urgente. A convenção pode parecer invertida, mas é a tradicional: "prioridade 1" é o topo da lista.
Retentativa por tarefa#
O retry fica em volta de uma tarefa. Uma falha não interrompe a fila — é registrada e o laço segue. Um monitor em volta do laço inteiro abortaria tudo na primeira falha.
Registrar o resultado, não só o sucesso#
Guardar o histórico completo é o que torna o relatório possível. Sem ele, você saberia que "algo falhou", mas não o quê nem por quê.
Reenfileirar#
Tarefas que falharam voltam com prioridade 0. Numa fila real, isso precisaria de um contador de tentativas para não gerar laço infinito com tarefas que sempre falham.