O modelo de eventos: triggers e integrações

TL;DR

Uma função Lambda nunca acorda sozinha — ela é sempre uma reação a um evento, e a AWS reconhece três formas fundamentalmente diferentes de entregar esse evento: síncrona (o chamador espera a resposta, como o API Gateway), assíncrona (o evento é enfileirado, o chamador recebe um 202 na hora e a execução acontece depois, com retry automático embutido, como S3 ou SNS) e poll-based via event source mapping (o próprio serviço Lambda fica de olho numa fila ou stream — SQS, Kinesis, DynamoDB Streams — e invoca a função em lotes). Cada modelo trata erro de um jeito diferente, e confundir os três é a origem da maioria dos bugs sutis em sistemas serverless: esperar resposta de uma invocação assíncrona, ignorar que um erro numa stream pode travar o processamento inteiro daquele pedaço da fila, ou duplicar efeitos porque o retry automático reenviou um evento que já tinha sido processado.

A função que reage

Pare por um segundo e repare no que uma função Lambda não tem: não tem um while True esperando trabalho, não tem uma porta aberta ouvindo conexões, não tem um processo em pé consumindo memória enquanto nada acontece. O código de uma função existe como definição — runtime, handler, variáveis de ambiente, papel de execução — mas só ganha um processo real no instante em que algo dispara uma invocação. Depois que a função termina de responder (ou de processar, no caso assíncrono), esse processo pode continuar vivo por um tempo aguardando a próxima invocação — é o que a próxima nota desta trilha vai chamar de warm start — mas a ideia central não muda: nada acontece por iniciativa da função. Tudo começa em outro lugar.

Isso é uma inversão real de modelo mental para quem vem de um serviço tradicional. Numa aplicação Spring Boot rodando numa EC2, o processo está de pé o tempo todo, e é ele quem decide quando ler da fila, quando fazer polling, quando aceitar uma conexão. No serverless, essa responsabilidade de “ficar de olho” muda de dono: alguém — ou algum serviço gerenciado — precisa entregar o evento à função. Esse “alguém” é o trigger, e a forma como ele entrega o evento — nem sempre óbvia à primeira vista — determina praticamente todo o comportamento observável do sistema: quem vê o erro, se há retry, se a ordem é preservada, se dois eventos podem ser processados em paralelo.

É por isso que, numa arquitetura serverless madura, o trigger recebe tanta atenção de design quanto o código da função em si. Escolher “S3 dispara a função” é uma decisão de arquitetura tão importante quanto escolher a linguagem do handler — porque essa escolha já embute um modelo de invocação inteiro, com suas próprias garantias e armadilhas.

flowchart LR
    subgraph Fontes["Fontes de evento"]
        AG["API Gateway / ALB"]
        S3E["S3"]
        SNS["SNS"]
        EB["EventBridge"]
        SQS["SQS"]
        KIN["Kinesis"]
        DDB["DynamoDB Streams"]
    end
    AG -->|"síncrono"| L["Função Lambda"]
    S3E -->|"assíncrono"| L
    SNS -->|"assíncrono"| L
    EB -->|"assíncrono"| L
    SQS -->|"poll-based"| L
    KIN -->|"poll-based"| L
    DDB -->|"poll-based"| L

Os três modelos de invocação

A documentação oficial da AWS é explícita: existem três formas distintas de invocar uma função Lambda, e cada uma tem seu próprio contrato de erro, sua própria semântica de retry e sua própria relação entre chamador e função.

Síncrono (request-response)

No modelo síncrono, quem invoca a função espera a resposta. A chamada Invoke da API do Lambda, com InvocationType=RequestResponse (o padrão), roda a função e devolve o resultado — ou o erro — diretamente para quem chamou. Se a função lançar uma exceção, quem invocou recebe status 200 da própria API Lambda (a chamada em si funcionou), mas com um campo indicando erro na função; o corpo da resposta carrega o erro, não um resultado de negócio.

aws lambda invoke --function-name minha-funcao \
    --cli-binary-format raw-in-base64-out \
    --payload '{ "key": "value" }' response.json
{
    "ExecutedVersion": "$LATEST",
    "StatusCode": 200
}

Serviços que invocam Lambda de forma síncrona incluem o API Gateway e o Application Load Balancer (ALB) — nos dois casos, existe um cliente HTTP real do outro lado esperando uma resposta, então não faria sentido enfileirar o pedido e responder “processando” sem mais. Nesse modelo, é o serviço que fez a chamada quem decide o que fazer com um erro: o API Gateway, por exemplo, sempre repassa o erro de volta ao cliente que fez a requisição HTTP — ele não retenta silenciosamente. É o modelo mais familiar de quem vem de APIs tradicionais, e por isso o mais fácil de entender errado nos outros dois casos: a intuição de “chamei, esperei, recebi resposta” simplesmente não existe nos modelos seguintes.

Assíncrono (event)

No modelo assíncrono, Lambda enfileira o evento internamente e devolve, na hora, uma confirmação de que o evento foi aceito — não o resultado do processamento, que ainda nem começou. Invocar de forma assíncrona pela CLI significa trocar InvocationType para Event:

aws lambda invoke \
  --function-name minha-funcao \
  --invocation-type Event \
  --cli-binary-format raw-in-base64-out \
  --payload '{ "key": "value" }' response.json
{
    "StatusCode": 202
}

O 202 é a assinatura visual do modelo assíncrono: “aceitei, vou processar, não pergunte agora.” Serviços como S3 (mudança em bucket) e SNS (mensagem publicada num tópico) invocam Lambda assim por padrão — eles não esperam, nem poderiam esperar de forma prática, o processamento de um evento antes de seguir em frente. O EventBridge, tratado adiante nesta nota, também dispara Lambda de forma assíncrona quando o alvo de uma regra é uma função.

A parte que separa o modelo assíncrono do síncrono de verdade é o que acontece depois do 202: se a função falhar, é a própria Lambda — não quem chamou — quem decide se e quantas vezes tenta de novo, porque quem chamou já foi embora satisfeito com o 202. Isso é tratado em detalhe na seção de tratamento de erro, adiante.

Poll-based / stream (event source mapping)

O terceiro modelo é o mais diferente dos outros dois, porque nele não é o serviço de origem quem invoca a Lambda — é a própria Lambda quem vai atrás do trabalho. Um recurso chamado event source mapping, gerenciado internamente pelo serviço Lambda, faz polling ativo numa fonte — uma fila SQS, um stream Kinesis, DynamoDB Streams, um tópico Kafka — junta os itens disponíveis em um lote (batch), e só então invoca a função de forma síncrona, passando o lote inteiro como payload.

sequenceDiagram
    participant Fonte as Fonte poll-based<br/>(SQS, Kinesis, DynamoDB Streams, Kafka)
    participant ESM as Event Source Mapping<br/>(event pollers do Lambda)
    participant L as Função Lambda

    loop Polling contínuo
        ESM->>Fonte: Há itens novos?
        Fonte-->>ESM: Sim — acumula até<br/>batch size ou batching window
    end
    ESM->>L: Invoke síncrono com o lote (batch)
    alt Sucesso
        L-->>ESM: OK — item processado
        ESM->>Fonte: Remove/avança (ex.: DeleteMessage no SQS)
    else Erro
        L-->>ESM: Erro na função
        ESM->>Fonte: Retenta o lote (stream)<br/>ou reporta falhas parciais (fila)
    end

A documentação oficial descreve os event pollers como recursos internos que “ativamente fazem polling de novas mensagens e invocam funções” — por padrão o Lambda escala esses pollers automaticamente conforme o volume, e para algumas fontes existe um modo provisionado, com número mínimo e máximo de pollers dedicados, para cargas que exigem latência mais consistente.

Vale a distinção que a própria AWS traça explicitamente entre trigger e event source mapping: com S3 ou SNS, o serviço de origem empurra o evento (é um trigger de verdade, configurado e armazenado no serviço de origem); com SQS, Kinesis, DynamoDB Streams e Kafka, é o Lambda quem puxa — o event source mapping é um recurso que vive dentro do próprio serviço Lambda, criado com CreateEventSourceMapping.

Event sources principais, um a um

Cada fonte de evento formata o payload de um jeito próprio e carrega semânticas diferentes — vale conhecer, ao menos por cima, o formato de cada uma antes de escrever o handler.

API Gateway (síncrono). O evento carrega o método HTTP, path, headers, query string params e corpo da requisição; a função devolve um objeto com statusCode, headers e body, que o API Gateway traduz de volta em resposta HTTP. É o padrão de toda API REST/HTTP serverless.

S3 (assíncrono). Dispara em eventos como ObjectCreated:Put, ObjectRemoved:Delete e outros — configurado como notificação de bucket, apontando para o ARN da função. O evento traz o nome do bucket e a chave do objeto:

{
  "Records": [
    {
      "eventVersion": "2.0",
      "eventSource": "aws:s3",
      "awsRegion": "us-east-1",
      "eventTime": "2026-07-24T00:00:00.000Z",
      "eventName": "ObjectCreated:Put",
      "s3": {
        "bucket": { "name": "meu-bucket", "arn": "arn:aws:s3:::meu-bucket" },
        "object": { "key": "uploads/foto.jpg", "size": 204800, "eTag": "abc123" }
      }
    }
  ]
}

O padrão de uso clássico é pipeline de processamento de arquivo: upload dispara a função, a função lê o objeto, processa (redimensiona, converte, valida) e grava o resultado — muitas vezes num segundo bucket, para evitar disparar a mesma função de novo em loop.

SQS (poll-based). Fila de mensagens ponto-a-ponto; cada mensagem é entregue a exatamente um consumidor por vez, dentro do período de visibility timeout. O evento chega como lote:

{
  "Records": [
    {
      "messageId": "059f36b4-87a3-44ab-83d2-661975830a7d",
      "body": "Pedido #4821 pronto para faturar",
      "attributes": { "ApproximateReceiveCount": "1" },
      "eventSource": "aws:sqs",
      "eventSourceARN": "arn:aws:sqs:us-east-2:123456789012:fila-pedidos"
    }
  ]
}

SNS (assíncrono, pub/sub). Um tópico SNS publica para múltiplos assinantes — Lambda é um deles entre vários possíveis (e-mail, SQS, HTTP). Cada mensagem publicada no tópico vira uma invocação assíncrona independente da função inscrita.

EventBridge (event bus / regras / schedule). É o barramento de eventos central da AWS: regras (rules) casam eventos por padrão (event pattern) e roteiam para um ou mais alvos, incluindo funções Lambda. Uma regra pode reagir a um evento de outro serviço da AWS ou rodar num horário fixo — é o substituto moderno do cron tradicional na AWS:

{
  "ScheduleExpression": "rate(15 minutes)",
  "State": "ENABLED",
  "Targets": [
    { "Arn": "arn:aws:lambda:us-east-1:123456789012:function:limpa-cache", "Id": "alvo-1" }
  ]
}

Ou, com expressão cron (sintaxe de seis campos, própria da AWS — minuto, hora, dia-do-mês, mês, dia-da-semana, ano):

aws events put-rule \
  --name relatorio-diario \
  --schedule-expression "cron(0 8 * * ? *)" \
  --state ENABLED

DynamoDB Streams (poll-based). Captura toda mudança (insert/update/delete) numa tabela DynamoDB, como um log ordenado por shard. O evento traz a imagem antiga e/ou nova do item, conforme o StreamViewType configurado — útil para replicar dados, invalidar cache ou disparar workflows a partir de uma escrita.

Kinesis Data Streams (poll-based). Semelhante em modelo ao DynamoDB Streams, mas para streaming de eventos de aplicação em alto volume — telemetria, cliques, logs — organizados em shards, com a mesma lógica de polling e retry de lote.

Event sourceModeloO que chega no eventoUso típico
API GatewaySíncronométodo, path, headers, query, bodyAPI REST/HTTP serverless
ALBSíncronorequisição HTTP do balanceadorbackend atrás de um load balancer existente
S3Assíncronobucket + chave do objeto + tipo de eventopipeline de processamento de arquivo
SNSAssíncronomensagem publicada no tópicofan-out pub/sub
EventBridgeAssíncronoevento estruturado ou disparo agendadoorquestração entre serviços, jobs cron
SQSPoll-basedlote de mensagens da filaprocessamento de fila, desacoplamento
Kinesis Data StreamsPoll-basedlote de registros do shardstreaming de alto volume
DynamoDB StreamsPoll-basedlote de mudanças (INSERT/MODIFY/REMOVE)reagir a escrita numa tabela
Kafka (MSK / self-managed)Poll-basedlote de mensagens do tópicointegração com stack de mensageria existente

Fronteira

O que é uma fila, o que é pub/sub, e por que um sistema opta por comunicação assíncrona entre serviços é conteúdo de Comunicação entre Sistemas — esta nota assume esse vocabulário e trata apenas de como esses eventos chegam a uma função Lambda. Os serviços gerenciados de mensageria da AWS em si — SQS, SNS e EventBridge, com filas FIFO, DLQ nativa da fila, padrões de arquitetura de evento — são o assunto de um galho futuro desta trilha Cloud (mensageria gerenciada), ainda não escrito; aqui eles aparecem só pelo ângulo de “como disparam uma função”.

Event source mapping: o recurso por trás do polling

Para as fontes poll-based, o vínculo entre a fila/stream e a função não é automático — é um recurso explícito, criado com create-event-source-mapping, que carrega toda a configuração de como o polling deve se comportar:

aws lambda create-event-source-mapping \
  --function-name processa-pedido \
  --event-source-arn arn:aws:sqs:us-east-2:123456789012:fila-pedidos \
  --batch-size 10 \
  --maximum-batching-window-in-seconds 5

Os parâmetros centrais, segundo a documentação de CreateEventSourceMapping:

  • BatchSize — o número máximo de registros por invocação. Para SQS, Kinesis e DynamoDB o padrão de batching window é 0 segundos (invoca assim que há registros); para Kafka e Amazon MQ, o padrão é 500 ms.
  • MaximumBatchingWindowInSeconds — quanto tempo o event source mapping espera acumular um lote antes de invocar, mesmo sem atingir o BatchSize — de 0 a 300 segundos.
  • Um lote é fechado e enviado quando qualquer um dos três critérios é atingido primeiro: a janela de tempo expira, o BatchSize é alcançado, ou o payload chega a 6 MB (limite fixo, não configurável).
aws lambda update-event-source-mapping \
  --uuid a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
  --batch-size 100 \
  --maximum-batching-window-in-seconds 10

Assista: A serverless journey: AWS Lambda under the hood (re:Invent 2019, SVS405-R1)

Canal: AWS Events | Duração: ~51min | Idioma: EN

Um mergulho na engenharia interna por trás do event source mapping: como o Lambda usa um stream tracker pra descobrir shards/partições de uma fonte como SQS ou Kinesis, decidir o batch size e invocar a função — o “quem faz o polling de verdade” por trás do recurso que você só declara com create-event-source-mapping. Trecho de destaque [12:07]: “for event sources such as sqs as an event source… you are providing the connection to the event source whether it’s your sqs queue or one of the other event sources lambda supports and lambda does the rest”

🎬 Assistir no YouTube

Partial batch response — não jogue o lote inteiro fora

Um detalhe que costuma pegar quem está começando: se a função processa 10 mensagens de um lote SQS e falha na nona, o comportamento padrão é tratar o lote inteiro como falho — as 10 mensagens voltam a ficar visíveis na fila depois do visibility timeout, incluindo as 8 que já tinham sido processadas com sucesso. Para evitar reprocessar o que já deu certo, a função pode devolver explicitamente quais itens falharam, usando partial batch response (ReportBatchItemFailures):

{
  "batchItemFailures": [
    { "itemIdentifier": "059f36b4-87a3-44ab-83d2-661975830a7d" }
  ]
}

Com essa configuração habilitada no event source mapping, o Lambda remove da fila apenas as mensagens que não aparecem em batchItemFailures — as demais voltam para retry, isoladamente. O mesmo padrão existe para DynamoDB Streams e Kinesis.

Concorrência e paralelismo por shard

Uma dúvida comum de quem está migrando um consumidor tradicional para event source mapping: quantas invocações da função rodam ao mesmo tempo? A resposta depende da fonte. Para SQS, o Lambda escala o número de pollers automaticamente conforme o volume da fila — mais mensagens chegando, mais concorrência, dentro do limite de concorrência da própria função (ou de MaximumConcurrency, se configurado no event source mapping). Para fontes baseadas em shard, como Kinesis e DynamoDB Streams, o paralelismo tem outra granularidade: por padrão, um shard só pode ter um lote em processamento por vez, exatamente para preservar a ordem dos registros dentro daquele shard. O parâmetro ParallelizationFactor (de 1 a 10) permite processar múltiplos lotes do mesmo shard simultaneamente — útil quando o volume de dados é alto e a métrica IteratorAge (o quão “atrasado” o consumo está em relação à ponta do stream) fica subindo — mas o Lambda garante que, mesmo com paralelismo maior que 1, a ordem por chave de partição continua sendo respeitada dentro daquele shard. Aumentar o número de shards da fonte, não só o ParallelizationFactor, costuma ser o primeiro lugar a olhar quando o gargalo é throughput, não latência de função.

Tratamento de erro por modelo

Este é o ponto onde os três modelos deixam de ser só uma questão de “quem chama quem” e passam a determinar diretamente o comportamento do sistema sob falha.

Síncrono. O erro volta imediatamente para quem chamou. Não há retry automático por parte do Lambda — se o API Gateway, o ALB, ou um SDK que chamou Invoke diretamente quiser tentar de novo, a decisão e a implementação do retry são inteiramente dele. A AWS CLI e os SDKs da AWS já retentam automaticamente em timeouts de cliente, throttling e erros de serviço — mas isso é comportamento do cliente, não do Lambda.

Assíncrono. Por padrão, se a função retornar erro, Lambda tenta de novo duas vezes — três tentativas no total — com aproximadamente um minuto de espera entre a primeira e a segunda tentativa, e dois minutos entre a segunda e a terceira. Para erros de throttling (429) e erros de sistema (500-series), o evento volta para a fila interna e o Lambda continua tentando por até seis horas por padrão, com backoff exponencial crescendo até um teto de cinco minutos entre tentativas. Passado esse prazo, ou esgotadas as tentativas configuradas, o evento é descartado — a menos que exista uma dead-letter queue (DLQ) ou um destino de falha (on-failure destination) configurado para capturá-lo:

aws lambda update-function-event-invoke-config \
  --function-name processa-webhook \
  --maximum-retry-attempts 1 \
  --maximum-event-age-in-seconds 3600 \
  --destination-config '{
    "OnFailure": { "Destination": "arn:aws:sqs:us-east-1:123456789012:falhas-webhook" }
  }'

Vale distinguir DLQ de destino de falha, porque não são a mesma coisa: uma DLQ clássica (configurada no nível da função, apontando para SQS ou SNS) captura só o evento original quando ele é descartado. Um on-failure destination (SQS, SNS, S3, EventBridge ou uma outra função Lambda) é mais rico — carrega metadados sobre a invocação que falhou, incluindo a razão (RetryAttemptsExhausted, por exemplo), o número de tentativas feitas, e, para destinos S3, o payload original completo. Destinos são a peça recomendada hoje; DLQ segue suportada por compatibilidade.

Poll-based / stream. Aqui o comportamento padrão é o mais perigoso dos três: se a função retorna erro processando um lote de um stream (Kinesis ou DynamoDB Streams), o Lambda retenta o lote inteiro, e para preservar a ordem de processamento, pausa o shard afetado até o erro ser resolvido ou os registros expirarem. Ou seja: um único registro “ruim” — malformado, que sempre derruba a função — pode travar o processamento de todo aquele shard, inclusive de registros bons que vieram depois dele, até que alguém intervenha ou os dados expirem. Com as configurações padrão, isso pode bloquear um shard por até um dia inteiro.

Duas configurações mitigam isso: MaximumRetryAttempts limita quantas vezes o Lambda tenta antes de desistir daquele lote, e BisectBatchOnFunctionError divide um lote com erro em dois lotes menores, isolando o registro problemático sem consumir a cota de retry:

aws lambda update-event-source-mapping \
  --uuid f89f8514-cdd9-4602-9e1f-01a5b77d449b \
  --maximum-retry-attempts 2 \
  --maximum-record-age-in-seconds 3600 \
  --bisect-batch-on-function-error \
  --destination-config '{"OnFailure": {"Destination": "arn:aws:sns:us-east-1:123456789012:falhas-stream"}}'

Para SQS, o mecanismo é diferente — não há “shard bloqueado” porque SQS não é ordenado por partição da mesma forma —, mas o princípio de isolar o item problemático é o mesmo: configure ReportBatchItemFailures (partial batch response, visto acima) e uma redrive policy na própria fila SQS, apontando para uma DLQ depois de N tentativas de entrega malsucedidas.

ModeloQuem decide retryOnde captura falha definitivaRisco característico
SíncronoQuem chamou (fora do Lambda)Nenhuma — erro volta na horaCliente sem tratamento de erro trava
AssíncronoLambda, automaticamente (2 retries padrão)DLQ ou on-failure destinationEfeito duplicado sem idempotência
Poll-based / streamEvent source mapping (config por MaximumRetryAttempts)On-failure destination do event source mappingPoison message bloqueia o shard inteiro

Casos práticos

A thumbnail que nunca chega. Um pipeline dispara uma função ao upload de imagem no S3, redimensiona e grava num segundo bucket. Se a imagem enviada estiver corrompida, a função lança exceção — como é invocação assíncrona, o Lambda tenta mais duas vezes automaticamente, sem que ninguém peça. Sem uma DLQ ou destino de falha configurado, depois da terceira tentativa o evento simplesmente desaparece — e ninguém no time sabe que aquele upload nunca virou thumbnail, até um usuário reclamar.

O shard que parou de andar. Uma função consome DynamoDB Streams para replicar mudanças de pedido para um índice de busca. Um dia, um item chega com um campo em formato inesperado, e o código de parsing lança exceção sem tratamento. Sem BisectBatchOnFunctionError nem limite de retry configurado, o shard inteiro para: todo pedido novo feito depois daquele item malformado fica sem replicar no índice de busca, silenciosamente, até alguém notar o IteratorAge subindo nos dashboards e investigar.

O webhook que rodou duas vezes. Um provedor de pagamento chama um endpoint HTTP que aciona, via API Gateway, uma função síncrona — até aqui, sem susto: erro volta na hora, sem retry automático do Lambda. Mas o próprio provedor de pagamento, ao não receber 200 a tempo (a função demorou por causa de cold start), reenvia o webhook pela sua própria política de retry externa. A função processa o mesmo evento de pagamento duas vezes — e sem uma verificação de idempotência (checar se aquele payment_id já foi processado antes de debitar de novo), o cliente é cobrado em dobro.

Lente dupla: AWS e o modelo mais restrito da DigitalOcean

A riqueza de event sources vista até aqui é uma característica particular da AWS — o Lambda tem mais de uma década de integrações acumuladas com praticamente todo serviço gerenciado da própria AWS. A DigitalOcean Functions, sendo uma plataforma bem mais nova e deliberadamente mais enxuta, não tenta reproduzir esse catálogo inteiro, e vale nomear a diferença com precisão em vez de fingir equivalência.

Segundo a documentação oficial da DigitalOcean, as funções são disparadas por dois mecanismos:

  • Web trigger (HTTP) — toda função DigitalOcean, ao ser publicada, ganha automaticamente um endpoint HTTP público (ou protegido por autenticação, configurável). É o equivalente funcional ao par API Gateway + Lambda síncrono da AWS, só que embutido por padrão, sem precisar provisionar um recurso separado de API Gateway.
  • Scheduled trigger (cron) — definido em project.yml ou pelo painel de controle, dispara a função periodicamente com sintaxe cron de cinco campos (minuto, hora, dia do mês, mês, dia da semana), podendo passar um payload JSON fixo a cada disparo. É o equivalente ao EventBridge com ScheduleExpression.

O que não existe — e essa é a diferença estrutural, não um detalhe menor — é o ecossistema de event sources da AWS: não há um equivalente a “bucket dispara função no upload” nativamente integrado ao Spaces (o serviço de armazenamento de objetos da DigitalOcean), não há event source mapping para uma fila gerenciada, não há um barramento de eventos tipo EventBridge reagindo a mudanças de outros serviços gerenciados. Se uma aplicação na DigitalOcean precisa do padrão “arquivo chegou → processa”, o caminho comum é a própria aplicação notificar a função via chamada HTTP ao web trigger — a orquestração que, na AWS, o S3 faz sozinho, na DigitalOcean cai por conta de código de aplicação.

# DigitalOcean — trigger agendado em project.yml (cron de 5 campos)
schedule:
  - name: limpa-cache-diario
    function: limpa-cache
    cron: "0 4 * * *"
    payload:
      modo: completo

Caducidade

A documentação da DigitalOcean, no momento da pesquisa, descreve scheduled triggers como recurso em private preview, com limite de até 3 triggers agendados por conta durante essa fase, e ainda não disponíveis para componentes de função publicados via App Platform. Isso é exatamente o tipo de limitação que tende a mudar rápido — confira a documentação oficial atual antes de arquitetar em cima desse limite.

ConceitoAWS LambdaDigitalOcean Functions
Trigger HTTPAPI Gateway / ALB (recurso separado, provisionado)Web trigger nativo (embutido ao publicar a função)
Trigger agendadoEventBridge Scheduler / regra com ScheduleExpressionScheduled trigger, cron de 5 campos em project.yml (private preview)
Trigger de armazenamento de objetosS3 event notification (nativo)Sem equivalente nativo — orquestração fica com a aplicação
Trigger de fila/streamEvent source mapping (SQS, Kinesis, DynamoDB Streams, Kafka)Sem equivalente — sem fila/stream gerenciado com integração direta
Barramento de eventos entre serviçosEventBridge (event bus + regras de roteamento)Sem equivalente

Tabela de tradução: Azure Functions e GCP Cloud Functions

ConceitoAWS LambdaAzure FunctionsGCP Cloud FunctionsDigitalOcean Functions
Trigger HTTPAPI Gateway / ALBHTTP triggerHTTP triggerWeb trigger
Trigger agendadoEventBridge SchedulerTimer trigger (cron NCronTab)Cloud Scheduler + EventarcScheduled trigger (cron 5 campos)
Trigger de storageS3 event notificationBlob trigger (Azure Storage)Eventarc (Cloud Storage)
Trigger de fila/streamEvent source mapping (SQS/Kinesis/DynamoDB Streams)Queue trigger / Event Hub triggerEventarc (Pub/Sub)
Conceito central de integraçãoBindings implícitos por tipo de eventoBindings — entrada/saída declaradas no function.jsonEventarc — barramento unificado sobre Cloud EventsWeb trigger + scheduled trigger, sem barramento próprio
Vocabulário chaveEvent source / event source mappingTrigger + Binding (dois conceitos separados)Event trigger via Eventarc (CloudEvents padrão)Trigger (termo único, dois tipos)

A Azure vale um comentário à parte: ela separa explicitamente trigger (o que inicia a execução) de binding (uma forma declarativa de ler ou escrever dados de/para outro serviço sem escrever código de integração manual) — um vocabulário que nem AWS nem GCP usam da mesma forma. A GCP, por sua vez, consolidou boa parte dos seus triggers de evento em torno do Eventarc, que usa o padrão aberto CloudEvents como formato de envelope — uma escolha deliberada de portabilidade que nem AWS nem Azure adotaram como formato nativo.

Poison message travando um shard inteiro

Num event source mapping de stream (Kinesis ou DynamoDB Streams), um único registro malformado que sempre derruba a função pode bloquear o processamento de todo o shard por até um dia com as configurações padrão — porque o Lambda retenta o lote inteiro para preservar ordem. Configure MaximumRetryAttempts e BisectBatchOnFunctionError desde o primeiro deploy, não depois do primeiro incidente.

Confundir invocação assíncrona com síncrona

Chamar Invoke com InvocationType=Event e depois ficar esperando um resultado de negócio na resposta é um erro de leitura do modelo: a resposta só confirma que o evento foi aceito (202), não que foi processado. Se o chamador precisa do resultado, o modelo é síncrono, não assíncrono — ou é preciso desenhar um mecanismo separado de callback/polling para saber quando o processamento assíncrono terminou.

Retry automático sem idempotência

Tanto o retry assíncrono padrão (duas tentativas) quanto o retry de event source mapping podem reenviar um evento que, na prática, já rodou parcialmente — Lambda documenta explicitamente que “duplicate processing of records can occur.” Qualquer efeito colateral que não seja seguro de repetir (debitar um cartão, enviar um e-mail, incrementar um contador sem checar se já foi incrementado) precisa de uma checagem de idempotência antes de agir — normalmente guardando o identificador do evento já processado em algum lugar consultável.

O que vem a seguir

Esta nota respondeu “o que dispara a função e o que acontece quando dá errado” — mas ficou de fora uma pergunta que qualquer engenheiro que já operou Lambda em produção faz cedo: por que a primeira invocação depois de um tempo parado demora visivelmente mais que as seguintes? A resposta — cold start, warm start, e o ciclo de vida do ambiente de execução por trás de cada invocação, seja ela síncrona, assíncrona ou vinda de um event source mapping — é o assunto da próxima nota desta trilha.

Fontes