O Problema da Execução Durável: Uma História Familiar
Imagine um processo de negócio com múltiplas etapas: validar uma reclamação, rodar verificações de segurança, processar um pagamento, enviar notificações. No meio do caminho, o servidor cai. O que acontece depois?
Em arquiteturas tradicionais, a resposta é muitas vezes "depende." Talvez a operação expire e dispare processamento duplicado. Talvez um estado parcial corrompa o que vem a seguir. Para workflows que duram minutos, horas ou até dias, interrupções são muito prováveis.
Esse é o problema da execução durável: garantir que um processo multi-etapas seja concluído de forma confiável, mesmo através de falhas, reinicializações e deploys. A indústria produziu várias soluções:
- Engines de orquestração externos (ex: Temporal, AWS Step Functions) — testados em batalha, mas exigem infraestrutura dedicada e introduzem uma dependência externa crítica.
- Serviços de workflow gerenciados em nuvem — eliminam a sobrecarga operacional, mas introduzem vendor lock-in e preocupações regulatórias.
- Sistemas caseiros baseados em fila — evitam dependências externas, mas trocam por complexidade própria: cada equipe implementa sua própria lógica de retry, gerenciamento de estado e fluxos de compensação.
O Airbnb enfrentou todos esses tradeoffs em várias equipes. Em vez de cada serviço reinventar a roda, eles construíram o Skipper — um engine de workflow embarcado que roda dentro de cada serviço, usando o banco de dados que o serviço já utiliza.
Fonte: Airbnb Engineering Blog

Design Central do Skipper: Workflows e Actions
O Skipper se baseia em duas abstrações:
- Workflows — definem a lógica de orquestração: o que acontece em qual ordem e sob quais condições.
- Actions — encapsulam operações individuais (chamadas de API, atualizações de banco, notificações). Cada action é automaticamente checkpointed, para que seu resultado sobreviva a crashes e reinicializações.
Um Exemplo Concreto
Aqui está um workflow durável para processar a revisão de fotos de um anúncio:
class ListingPublicationWorkflow : Workflow() {
private val actions = actions()
@StateField val photosApproved: Boolean? = false
@WorkflowMethod
suspend fun publishListing(submission: ListingSubmission): PublicationResult {
// Envia fotos para revisão
val reviewId = actions.submitPhotosForReview(submission.getListingId())
// Aguarda a conclusão da revisão (manual ou automatizada)
val reviewTimedOut = waitUntil(() -> photosApproved != null,
Duration.ofHours(24))
if (reviewTimedOut || !photosApproved) {
actions.notifyHost(submission.getHostId(), "Fotos precisam de ajustes")
return PublicationResult.rejected("Revisão de fotos falhou")
}
// Publica o anúncio
actions.activateListing(submission.getListingId())
actions.notifyHost(submission.getHostId(), "Seu anúncio está no ar!")
return PublicationResult.success(submission.getListingId())
}
@SignalMethod
fun completePhotoReview(approved: Boolean) {
photosApproved = approved
}
}
Esse código é natural: enviar fotos, aguardar revisão, publicar. Não há lógica de retry, gerenciamento de fila ou coordenação assíncrona visível no workflow.
Como a Durabilidade Funciona
Quando um workflow inicia, o Skipper faz checkpoint do resultado de cada action no banco de dados. Se o workflow precisar esperar (via waitUntil), o Skipper persiste o estado atual e o workflow hiberna, sem consumir recursos computacionais.
Quando as condições mudam — um sinal chega, um timer expira ou o serviço reinicia — o Skipper reproduz o método do workflow desde o início. Actions já executadas não são reexecutadas; elas retornam seus resultados checkpointed instantaneamente. O workflow continua de onde parou.
Isso é fundamentalmente diferente de sistemas de orquestração event-sourced. O Skipper persiste campos de estado diretamente — não há um log de eventos para reproduzir. Isso torna a execução mais leve, especialmente para workflows com muitos sinais ou históricos longos, embora troque alguma auditabilidade por essa eficiência.

O Caminho Feliz: Saindo do Caminho
A maioria dos engines de workflow impõe overhead em toda execução, mesmo quando nada dá errado. Engines de orquestração externos exigem round-trips de rede para um cluster central para cada invocação de atividade.
O Skipper adota uma abordagem diferente. Quando um workflow inicia, duas coisas acontecem no nível do banco de dados:
- A instância do workflow é criada
- Uma tarefa de timeout atrasada é agendada como garantia de durabilidade
Então o workflow executa inteiramente in-process. Actions rodam como chamadas de método normais em uma fila de execução em memória, checkpoints são agrupados, e o workflow pode ser concluído sem qualquer coordenação adicional.
A tarefa atrasada funciona como uma rede de segurança: se o processo cair no meio da execução, o agendador persistente retoma o workflow após o período de lease expirar e o reproduz. Se o workflow for concluído normalmente, a tarefa de timeout é descartada inofensivamente.
Resultado: No caso feliz (sem crashes), o Skipper adiciona muito pouco overhead — apenas algumas escritas no banco. O workflow executa quase como se não houvesse engine de workflow algum.
Tradeoffs Principais
| O Que Você Ganha | O Que Você Troca |
|---|---|
| Sem infraestrutura para gerenciar (sem cluster separado) | Métodos de workflow devem ser determinísticos (sem efeitos colaterais, aleatoriedade ou lógica dependente de tempo) |
| Usa banco de dados existente (MySQL, DynamoDB) | Execução pelo menos uma vez — actions podem executar mais de uma vez em casos extremos; actions devem ser idempotentes |
| Modelo de programação simples (classes Java/Kotlin) | Complexidade de evolução — mudar a estrutura de um workflow pode quebrar workflows em andamento; estratégias de versionamento são necessárias |
| Escalabilidade independente por serviço | Não adequado para orquestração cross-language ou cross-service |
Limitações e Cuidados
- Determinismo é difícil. Desenvolvedores novos no padrão frequentemente introduzem não-determinismo acidentalmente (ex: chamar
System.currentTimeMillis()dentro de um método de workflow). Isso quebra a reprodução. - Idempotência é obrigatória. Como actions podem executar mais de uma vez, toda action deve ser segura para rodar múltiplas vezes.
- Versionamento de workflow é doloroso. Não há ferramentas de migração embutidas. Equipes precisam criar novas versões de métodos, migrar tráfego e depreciar versões antigas.
- Debugging é mais difícil. Timestamps de log e sequências de chamadas refletem reproduções, não a execução original. Melhores ferramentas de observabilidade (visualização de reprodução) ajudariam.
Próximos Passos para Aprendizado
Se esse padrão te interessou, considere explorar:
- Temporal — o engine de orquestração externo mais popular para execução durável
- AWS Step Functions — uma alternativa gerenciada em nuvem
- Apache Airflow — para orquestração de pipelines de dados
Para um mergulho prático em sistemas distribuídos na AWS, veja nosso guia sobre escalando Python com Ray em cluster na nuvem.

Conclusão: Quando Usar um Engine de Workflow Embarcado
Execução durável de workflow é uma capacidade fundamental para sistemas distribuídos confiáveis. O Skipper representa um ponto específico no espaço de design: um engine embarcado que troca orquestração centralizada por simplicidade operacional.
Essa abordagem não serve para todas as situações. Mas para serviços que buscam execução durável sem overhead de infraestrutura — particularmente onde minimizar dependências é primordial — o modelo embarcado oferece vantagens convincentes.
O insight central se generaliza além da implementação do Airbnb: execução baseada em reprodução com actions checkpointed pode fornecer durabilidade sem serviços de coordenação.