Lab 74 — Servir: tempo real, serverless, assíncrono ou lote
O problema, e a empresa que o tem
A Cadência treina, desde o L73, um modelo que prevê a probabilidade de um pedido ser cancelado — entrada em produção assim que o experimento rastreável daquele módulo produz um artefato reproduzível. O modelo foi treinado com uma rede que se beneficia de GPU, e desde então roda atrás de um único endpoint SageMaker (ml.g4dn.xlarge), ligado 24 horas por dia, sete dias por semana.
O problema não é o modelo. É que esse endpoint atende três chamadores com necessidades completamente diferentes: o painel de atendimento, que consulta a pontuação quando um vendedor abre um pedido (raro, mas bloqueia a tela até responder); o Sistema de Pedidos, que reavalia a pontuação a cada mudança de status — pagamento aprovado, por exemplo — e fica esperando a resposta antes de confirmar o próprio evento; e um script de análise ad-hoc do time de dados, rodado poucas vezes por semana.
Somando os três, são cerca de 200 chamadas por dia contra um endpoint de GPU que nunca desliga. Cada inferência usa em torno de 180 ms de computação real. Ninguém tinha ido medir o que isso significava em horas pagas até este laboratório — e, separadamente, ninguém tinha tentado pontuar de uma vez os 3.400 pedidos abertos que a equipe de retenção acompanha, porque fazer isso chamada por chamada contra o mesmo endpoint seria lento e caro demais para valer a pena.
O que este laboratório NÃO é
Não é sobre escolher o algoritmo do modelo, nem sobre re-treiná-lo — isso já está resolvido pelo tema do L73. Não é sobre versionar e promover modelos com aprovação e rollback — isso é o L75. Não é sobre consumir o endpoint do lado do cliente com timeout, retry e fallback — isso é o L79, que depende deste módulo. Este laboratório resolve uma coisa: dado um modelo já treinado, QUAL dos quatro modos de servir cada consumidor usa, medido em custo e latência — não como o modelo aprende, e não como ele é promovido.
O que você vai conseguir fazer
Objetivos verificáveis: cada um se prova com uma medição concreta na seção de implantação.
- Explicar por que um único endpoint de tempo real atende mal a padrões de tráfego opostos — bloqueante e raro, bloqueante e frequente, tolerante a espera.
- Configurar um endpoint de tempo real síncrono com janela de disponibilidade reduzida, para o chamador que bloqueia e não pode esperar.
- Configurar um endpoint serverless sobre uma versão do modelo compilada para CPU, e medir o custo real do cold start.
- Configurar um endpoint assíncrono com fila própria, S3 de entrada e saída, e notificação de conclusão via SNS.
- Configurar um job de Batch Transform que pontua milhares de registros de uma vez, sem manter endpoint ligado depois.
- Medir, para os quatro modos, horas-instância pagas por mês e latência p50/p99 — com número, não estimativa.
- Justificar por escrito qual modo cada consumidor da Cadência deveria usar, a partir do requisito dele, não da familiaridade com a configuração.
- Reconhecer o limite real de cada modo — em especial a ausência de suporte a GPU no serverless — antes de prometer um SLA que o modo não cumpre.
O que a certificação cobra disto
| Conceito | Certificação | Como aparece aqui | O que dominar |
|---|---|---|---|
| SageMaker real-time endpoints | MLA-C01, MLS-C01 | endpoint ml.g4dn.xlarge com janela de disponibilidade reduzida, scale-to-zero fora do expediente | quando latência baixa e previsível vale mais que economizar hora ociosa — e como scale-to-zero muda esse cálculo sem eliminar cold start |
| SageMaker Serverless Inference | MLA-C01 | endpoint sem instância dedicada, MemorySizeInMB + MaxConcurrency, rodando a versão do modelo em CPU | os limites reais do modo — sem GPU, memória e concorrência com teto — e por que isso é a pegadinha mais comum de prova sobre este tema |
| SageMaker Asynchronous Inference | MLA-C01, MLS-C01 | fila interna do SageMaker atrás de InputLocation/OutputLocation no S3, notificação de conclusão via SNS | payload grande, processamento não-bloqueante, e como o auto scaling a zero funciona quando a fila esvazia |
| SageMaker Batch Transform | MLA-C01, MLS-C01 | create_transform_job lendo um prefixo do S3 de uma vez, sem endpoint persistente depois | a diferença entre "endpoint" e "job" — Batch Transform não fica no ar; ele processa e termina sozinho |
| Escolha de modo por custo e latência | MLA-C01, MLS-C01, AIF-C01 (conceito geral) | tabela de decisão da seção de implantação, com horas-instância e percentil de latência medidos para cada modo | dado um cenário de prova, reconhecer qual dos quatro modos atende o requisito descrito — é a competência mais cobrada deste tema |
Requisitos, e como cada um muda o desenho
Requisito que não muda uma linha de configuração é intenção, não requisito. A terceira coluna é onde cada um deixou marca no desenho.
| Requisito | Valor declarado | O que ele decide no desenho |
|---|---|---|
| Chamada do vendedor não pode travar a tela | resposta em até 300 ms, sempre | obriga endpoint de tempo real sempre pronto na janela de uso — nunca serverless (cold start) nem assíncrono (não-bloqueante por natureza) |
| Webhook de mudança de status não pode ficar bloqueado esperando o modelo | resposta ao webhook em milissegundos, independente de quando a pontuação sai | obriga modo assíncrono, com fila absorvendo o pico e resposta do modelo desacoplada da resposta do evento |
| Chamada de análise é rara e tolera espera | menos de 1 chamada por dia em média | obriga um modo que não cobre hora parada — mas isso exclui GPU, então exige uma versão do modelo compilada para CPU |
| Pontuar todos os pedidos abertos de uma vez, fora do pico | 3.400 pedidos, uma vez por noite, prontos antes das 8h | obriga Batch Transform — o único dos quatro pensado para lote com throughput alto e sem endpoint permanente |
| Custo não pode ser dominado por hora ociosa | redução medida em horas-instância pagas por mês, sem perder nenhum caso de uso | obriga medir utilização real por modo ANTES de decidir o dimensionamento — não depois |
| Cada modo precisa de observabilidade própria, comparável | latência e custo separados por endpoint/job, não uma métrica agregada única | obriga métricas no CloudWatch marcadas por variant/endpoint/job, e uma tabela de comparação lado a lado |
Arquitetura mínima: o endpoint que atende todo mundo do mesmo jeito
Este é o desenho que a Cadência tem desde que o modelo do L73 entrou em produção, e ele é um ponto de partida legítimo: um único endpoint resolve "o modelo responde" com poucas peças. O defeito não é ter um endpoint — é não ter perguntado, para cada chamador, se esse é o modo certo.
- → chamada síncrona quando o vendedor abre um pedido
- → chamada síncrona a cada mudança de status, bloqueando o próprio webhook até a resposta
- → chamada esporádica do time de dados, no mesmo endpoint de GPU dos outros dois
- → métrica de utilização de GPU, gerada a cada minuto e nunca consultada
- Fora da AWS
- IA e machine learning
- Gestão e governança
As três setas chegam no mesmo lugar porque é o único caminho que existe — não porque é o caminho certo para as três. Percorra os passos e repare que o defeito não é "GPU cara": é decidir infraestrutura pelo que é mais fácil de configurar, não pelo padrão real de cada chamador.
- O vendedor abre um pedido e o painel de atendimento chama o endpoint. Poucas vezes por dia — mas a resposta precisa vir em tempo de não travar a tela de quem está atendendo o cliente.
- O Sistema de Pedidos chama o MESMO endpoint a cada mudança de status. Essa chamada é muito mais frequente que a do vendedor, e o próprio webhook de pagamento fica esperando a resposta do modelo antes de seguir.
- O script de análise ad-hoc também bate no mesmo endpoint. Poucas vezes por semana, tolera esperar — mas como não existe outro caminho, entra na fila do mesmo endpoint de GPU dos dois anteriores.
- O endpoint GPU fica ligado 24 horas por dia, todo santo dia. Precisa estar pronto para qualquer um dos três chamadores, a qualquer hora — mesmo que dois deles só apareçam em horário comercial e o terceiro poucas vezes por semana.
- CloudWatch registra a utilização real da GPU. A métrica sempre existiu; o que faltava era alguém perguntar o que ela dizia. É essa pergunta que abre a seção de implantação deste módulo.
- Três padrões de tráfego opostos, comprimidos numa única decisão de infraestrutura. Bloqueante e raro, bloqueante e frequente, tolerante a espera — nenhum modo único de servir atende bem aos três ao mesmo tempo sem desperdiçar em pelo menos dois deles.
# Mede o que ninguem tinha medido: quantos minutos de GPU sao usados de fato
# num mes de 30 dias, contra quantas horas o endpoint fica ligado.
CHAMADAS_DIA=200
MS_POR_CHAMADA=180
# Computacao real usada por dia, em segundos
python3 - <<'PY'
chamadas_dia = 200
ms_por_chamada = 180
segundos_dia = chamadas_dia * ms_por_chamada / 1000
minutos_mes = segundos_dia * 30 / 60
horas_pagas_mes = 24 * 30
minutos_pagos_mes = horas_pagas_mes * 60
utilizacao = minutos_mes / minutos_pagos_mes * 100
print(f'computacao real: {segundos_dia:.0f}s/dia -> {minutos_mes:.0f} min/mes')
print(f'pago: {horas_pagas_mes} h/mes ({minutos_pagos_mes:.0f} min/mes)')
print(f'utilizacao real da GPU: {utilizacao:.2f}%')
PY
# Esperado: computacao real ~18 min/mes contra 43.200 min/mes pagos -- 0,04% de utilizacao.
# O endpoint nao esta "caro": esta ligado por um motivo que nunca foi escrito.
A GPU não está ociosa por acidente — está ociosa porque ninguém decidiu quando ela precisa estar acesa
Ligar 24 horas por dia para atender 200 chamadas de 180 ms cada não é um erro de configuração — é a ausência de uma decisão. O endpoint responde igualmente bem à 1 chamada das 3h da manhã do script de análise e à chamada das 14h do vendedor, porque nunca ninguém perguntou se as duas precisam do mesmo tratamento. Elas não precisam.
Arquitetura para produção
Cada peça nova abaixo rastreia a uma linha da tabela de requisitos. A troca não é "adicionar mais um endpoint" — é substituir "um caminho para todo mundo" por "um caminho por padrão de tráfego", com o custo de cada um medido separadamente.
- → chamada síncrona, aguarda resposta em até 300 ms para não travar a tela do vendedor
- → chamada esporádica contra a versão do modelo compilada para CPU
- → publica pedido de reavaliação a cada mudança de status, sem esperar resposta
- → consome a fila em lotes pequenos
- → chama InvokeEndpointAsync com o payload gravado no S3 de entrada
- → grava o resultado assim que termina de processar o item
- → publica notificação de conclusão ou de erro
- → avisa que a pontuação ficou pronta
- → dispara o job às 2h, fora da janela de tráfego interativo
- → lê os pedidos abertos de uma vez e grava a pontuação de cada um
- → consulta a pontuação pronta da noite anterior, sem chamar o modelo
- → latência e contagem de invocação, medidas continuamente
- → latência por invocação, incluindo cold start
- → tempo de fila mais tempo de processamento
- → duração do job e throughput de linhas processadas
- → utilização por modo alimenta a comparação de custo
- Fora da AWS
- IA e machine learning
- Integração de apps
- Compute
- Armazenamento
- Analytics
- Gestão e governança
A diferença não é uma caixa a mais no mesmo desenho: o destino da chamada agora depende de quem chama. Bloqueante e raro vai para tempo real; esporádico e tolerante vai para serverless (em CPU); não-bloqueante vai para assíncrono via fila; lote vai para Batch Transform, que nem existe como endpoint. Percorra os passos: cada modo resolve o requisito de UM consumidor específico da arquitetura mínima.
- O painel de atendimento continua em tempo real síncrono. É o único consumidor que bloqueia a tela do vendedor e não tolera esperar — o requisito de latência baixa exclui os outros três modos para esse caminho específico.
- O script de análise migra para serverless, sobre uma versão do modelo em CPU. Serverless Inference não aceita GPU — a única forma de usar esse modo é registrar uma segunda versão do modelo compilada para CPU, aceitando latência um pouco maior em troca de zero hora paga ociosa.
- O Sistema de Pedidos para de esperar o modelo. Publica na fila e segue com o próprio fluxo de pagamento; quem absorve o pico e processa é o par fila mais função consumidora, desacoplado do webhook original.
- O endpoint assíncrono processa quando pode, grava o resultado e avisa por SNS. Ninguém ficou esperando a resposta: o resultado chega no S3, e uma notificação assíncrona informa o Sistema de Pedidos quando a pontuação está pronta.
- O painel de retenção sai do "pedido por pedido contra o endpoint". Vira uma consulta ao resultado de um job de Batch Transform disparado à noite — os 3.400 pedidos abertos são pontuados de uma vez, em paralelo, sem manter nenhum endpoint ligado depois.
- Cada modo publica sua própria métrica de latência no CloudWatch. Comparável lado a lado — porque a pergunta certa nunca foi "o modelo é rápido", foi "esse modo é rápido o bastante para quem chama ele".
- Cost Explorer fecha o ciclo com a fatura dividida por modo. É aí que a redução de horas-instância pagas se prova com número — não com estimativa — e é o que a seção de implantação mede.
A diferença estrutural em relação à arquitetura mínima não é a contagem de peças: é que o destino da chamada deixou de ser fixo. Antes, toda chamada ia para o mesmo endpoint, rápida ou lenta, bloqueante ou não; agora o PADRÃO DE TRÁFEGO de quem chama decide o caminho.
Scale-to-zero no tempo real não substitui os outros três modos
Endpoints de tempo real do SageMaker ganharam suporte a escalar para zero instâncias quando ociosos — isso reduz o custo do modo 1 fora do horário configurado, mas não resolve a decisão deste laboratório: um endpoint com scale-to-zero ainda paga cold start ao acordar (como o serverless), ainda bloqueia quem chama enquanto processa (diferente do assíncrono), e ainda cobra por invocação individual, sem o paralelismo em lote do Batch Transform. A pergunta certa nunca foi "dá para zerar o ocioso", foi "qual padrão de tráfego cada modo atende".
O ganho que aparece na fatura, e o que não aparece nela
A redução de horas-instância pagas por mês é mensurável e está na seção de implantação. O que não aparece em nenhuma fatura é a capacidade nova: pontuar os 3.400 pedidos abertos de uma vez, todas as noites, não existia antes — não porque fosse proibido, mas porque fazer isso chamada por chamada contra um endpoint de tempo real nunca teria sido rápido nem barato o suficiente para alguém tentar.
O caminho do dado, ponta a ponta: o caso assíncrono
O caminho assíncrono é o mais fácil de entender errado, porque parece "tempo real, só que mais devagar". Não é: ele existe porque o Sistema de Pedidos NÃO PODE ficar esperando, e o fluxo abaixo mostra exatamente onde essa espera deixa de existir.
Payload que a funcao consumidora grava no S3 de entrada, antes de chamar invoke_endpoint_async. O endpoint le esse arquivo, nao um corpo inline.
{
"pedido_id": "PED-88213",
"loja_id": "L07",
"valor_total": 412.90,
"forma_pagamento": "pix",
"status_anterior": "aguardando_pagamento",
"status_novo": "pagamento_aprovado",
"historico_pedidos_cliente": 4,
"tempo_desde_criacao_min": 18
}
A latência maior do modo assíncrono é o preço certo, não um defeito
p99 de alguns segundos no caminho assíncrono pareceria inaceitável se comparado ao tempo real — e seria, para o painel de atendimento. Para o Sistema de Pedidos, o requisito nunca foi "responda rápido"; foi "não me obrigue a esperar". Os dois requisitos parecem parecidos e não são, e é essa diferença que decide o modo.
As decisões, e o que se perde em cada uma
📋 Um modelo de previsão de cancelamento já treinado (L73), servindo três chamadores com padrões de tráfego opostos — mais um caso de pontuação em lote que hoje não existe — sobre um único endpoint de GPU sempre ligado.
Cada modo resolve o requisito de UM consumidor sem forçar os outros três a herdar o custo ou a latência dele. O tempo real continua existindo — só que dimensionado para o padrão real de quem precisa dele, não para os quatro casos ao mesmo tempo. Nenhum modo novo foi inventado: os quatro já existem nativamente no SageMaker, e a decisão foi rotear, não construir.
Alt: Manter um único endpoint de tempo real, só trocando o tipo de instância — resolve custo de instância, não resolve a mistura de padrões — o Sistema de Pedidos continua bloqueado, e pontuar 3.400 pedidos de uma vez continua impraticável chamada por chamada
Alt: Migrar tudo para Batch Transform, inclusive o caso do vendedor — o vendedor esperaria a próxima janela do job — minutos a horas — para ver uma pontuação que precisa aparecer na hora em que ele abre o pedido
Alt: Construir fila e cache próprios na frente de um único endpoint, sem usar os modos nativos — reimplementa, com mais código para manter, o que Async Inference e Batch Transform já fazem nativamente: fila gerenciada, auto scaling a zero, notificação de conclusão
Alt: Escalar o endpoint de tempo real horizontalmente para absorver tudo, incluindo o lote — 3.400 chamadas sequenciais contra um endpoint de tempo real, mesmo com várias instâncias, ainda paga por chamada individual e não usa o paralelismo em lote que o Batch Transform faz nativamente
| Decisão | Escolha | Alternativa considerada | Motivo | O que se perde |
|---|---|---|---|---|
| Onde a chamada do vendedor vai | tempo real síncrono, com janela de disponibilidade reduzida | manter ligado 24h como hoje | reduz de 720 para cerca de 270 horas-instância pagas por mês, sem mudar a latência que o vendedor vê | exige configurar corretamente a janela de scale-to-zero, e ajustá-la se o horário real de uso mudar |
| Onde o webhook de pagamento vai | assíncrono, com fila e notificação | manter síncrono contra o endpoint de tempo real | o webhook para de ficar bloqueado esperando o modelo — a latência da pontuação deixa de ser latência do pagamento | complexidade operacional: mais duas peças (fila e função consumidora) para monitorar |
| Onde o script de análise vai | serverless, sobre uma versão do modelo em CPU | manter no mesmo endpoint de GPU | zero hora paga ociosa para menos de 1 chamada por dia | uma segunda versão do modelo para manter versionada, além da de GPU |
| Onde o lote noturno vai | Batch Transform, disparado por agendador | chamar o endpoint de tempo real 3.400 vezes em sequência | processa em paralelo entre instâncias e termina sozinho — sem endpoint ligado depois do job | a pontuação só fica disponível na janela do job, nunca em tempo real |
A dívida que este módulo cria, e que ele não paga
A escolha de modo por consumidor é estática — decidida uma vez, na tabela de requisitos. Se o padrão de tráfego de um consumidor mudar (o script de análise de repente vira uma rotina horária, por exemplo), ninguém detecta isso automaticamente: alguém precisa notar e reconfigurar. Decidir o modo dinamicamente, a partir do tráfego observado, é otimização fora do escopo deste laboratório — e só valeria a pena numa escala muito maior que a da Cadência hoje.
Construir: tempo real síncrono, para quem não pode esperar
A peça nova em relação ao endpoint "sempre ligado" de antes não é o tipo de instância — é a janela de disponibilidade. `min_instance_count = 0` fora do horário configurado é o que transforma 720 horas pagas por mês em cerca de 270.
# endpoint-tempo-real.tf -- endpoint sincrono com janela reduzida
resource "aws_sagemaker_endpoint_configuration" "tempo_real" {
name = "${var.projeto}-cancelamento-tempo-real"
production_variants {
variant_name = "principal"
model_name = aws_sagemaker_model.cancelamento_gpu.name
instance_type = "ml.g4dn.xlarge"
initial_instance_count = 1
# Recurso relativamente recente do provider AWS -- confira suporte na sua
# versao antes de aplicar. Fora da janela, o SageMaker escala para zero
# instancias; a primeira chamada apos o periodo ocioso paga cold start.
managed_instance_scaling {
status = "ENABLED"
min_instance_count = 0
max_instance_count = 2
}
}
tags = { Projeto = var.projeto, Time = "dados", Modo = "tempo-real" }
}
resource "aws_sagemaker_endpoint" "tempo_real" {
name = "${var.projeto}-cancelamento-tempo-real"
endpoint_config_name = aws_sagemaker_endpoint_configuration.tempo_real.name
}
# EventBridge Scheduler acorda o endpoint 15 min antes do horario comercial,
# fazendo uma chamada de aquecimento -- paga o cold start ANTES do vendedor
# precisar da resposta, nao durante.
resource "aws_scheduler_schedule" "aquecer_tempo_real" {
name = "${var.projeto}-aquecer-endpoint-tempo-real"
group_name = "default"
flexible_time_window { mode = "OFF" }
schedule_expression = "cron(45 8 ? * MON-FRI *)"
target {
arn = aws_lambda_function.aquecer_endpoint.arn
role_arn = aws_iam_role.scheduler_invoca_lambda.arn
}
}
// ClienteTempoReal.cs -- chamada sincrona, com timeout curto e SEM retry.
// Retry aqui estouraria o orcamento de latencia do vendedor; o L79 trata
// disso com fallback -- este modulo so mede o caminho feliz.
using Amazon.SageMakerRuntime;
using Amazon.SageMakerRuntime.Model;
namespace Cadencia.Cancelamento;
public class ClienteTempoReal
{
private readonly AmazonSageMakerRuntimeClient _cliente = new(new AmazonSageMakerRuntimeConfig
{
Timeout = TimeSpan.FromMilliseconds(300), // orcamento do vendedor, nao negociavel aqui
});
public async Task<double> PontuarAsync(string payloadJson)
{
var resposta = await _cliente.InvokeEndpointAsync(new InvokeEndpointRequest
{
EndpointName = "cadencia-cancelamento-tempo-real",
ContentType = "application/json",
Body = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(payloadJson)),
});
using var leitor = new StreamReader(resposta.Body);
var corpo = await leitor.ReadToEndAsync();
return System.Text.Json.JsonDocument.Parse(corpo).RootElement.GetProperty("score").GetDouble();
}
}
Janela reduzida corta horas pagas, mas não corta cold start fora dela
Se um vendedor abrir um pedido às 8h40, cinco minutos antes da chamada de aquecimento programada, ele paga o cold start na cara. A janela de scale-to-zero precisa ter folga sobre o horário real de uso, não sobre o horário comercial nominal — é um dos três diagnósticos da seção de troubleshooting deste módulo.
Construir: serverless, para quem não paga hora parada
A restrição que mais gente descobre tarde: Serverless Inference não aceita instância com GPU. Servir o mesmo modelo do endpoint de tempo real exige uma segunda versão, compilada para CPU — não é o mesmo artefato do L73 rodando num container diferente.
# endpoint-serverless.tf -- sem instancia dedicada, so memoria e concorrencia
resource "aws_sagemaker_model" "cancelamento_cpu" {
name = "${var.projeto}-cancelamento-cpu"
execution_role_arn = aws_iam_role.sagemaker_execucao.arn
primary_container {
image = var.imagem_inferencia_cpu # container CPU-only, ONNX Runtime
model_data_url = var.artefato_modelo_cpu_s3 # versao COMPILADA para CPU, nao a de GPU do L73
}
tags = { Projeto = var.projeto, Time = "dados", Modo = "serverless" }
}
resource "aws_sagemaker_endpoint_configuration" "serverless" {
name = "${var.projeto}-cancelamento-serverless"
production_variants {
variant_name = "principal"
model_name = aws_sagemaker_model.cancelamento_cpu.name
serverless_config {
memory_size_in_mb = 3072 # so aceita valores fixos: 1024, 2048, 3072, 4096, 5120, 6144
max_concurrency = 5 # confira o teto da sua conta em Service Quotas antes de subir isso
}
}
}
resource "aws_sagemaker_endpoint" "serverless" {
name = "${var.projeto}-cancelamento-serverless"
endpoint_config_name = aws_sagemaker_endpoint_configuration.serverless.name
}
# medir_cold_start.py -- chama duas vezes seguidas e compara: a primeira
# paga o container subindo, a segunda encontra tudo quente.
import time
import json
import boto3
runtime = boto3.client("sagemaker-runtime")
ENDPOINT = "cadencia-cancelamento-serverless"
PAYLOAD = json.dumps({"pedido_id": "PED-TESTE", "valor_total": 199.90}).encode()
def invocar():
inicio = time.perf_counter()
runtime.invoke_endpoint(EndpointName=ENDPOINT, ContentType="application/json", Body=PAYLOAD)
return (time.perf_counter() - inicio) * 1000
# Espera ficar ocioso por tempo suficiente para o container ser reciclado
# antes de rodar este script -- senao as duas chamadas medem a mesma coisa.
fria = invocar()
time.sleep(1)
quente = invocar()
print(f"chamada fria (cold start): {fria:.0f} ms")
print(f"chamada quente (container ja de pe): {quente:.0f} ms")
Prometer o mesmo SLA do endpoint de tempo real no serverless é prometer o que o modo não cumpre
p99 do serverless inclui cold start, que pode passar de 2 segundos dependendo do tamanho do container. Se algum consumidor futuro migrar para serverless esperando a mesma latência do tempo real, ele vai quebrar na primeira janela de ociosidade — e o defeito não vai parecer bug, vai parecer "às vezes trava".
Construir: assíncrono, para quem não pode travar
A peça que faz o modo assíncrono valer a pena não é o endpoint — é a fila na frente dele. Sem ela, o Sistema de Pedidos ainda chamaria o SageMaker diretamente e ficaria esperando o OutputLocation aparecer, só que de um jeito mais complicado que síncrono.
# endpoint-assincrono.tf -- fila, endpoint assincrono, e as duas notificacoes
resource "aws_sqs_queue" "reavaliacao" {
name = "${var.projeto}-reavaliacao-pedidos"
visibility_timeout_seconds = 60
kms_master_key_id = aws_kms_key.lake.arn
}
resource "aws_sns_topic" "async_sucesso" { name = "${var.projeto}-cancelamento-async-sucesso" }
resource "aws_sns_topic" "async_erro" { name = "${var.projeto}-cancelamento-async-erro" }
resource "aws_sagemaker_endpoint_configuration" "assincrono" {
name = "${var.projeto}-cancelamento-assincrono"
production_variants {
variant_name = "principal"
model_name = aws_sagemaker_model.cancelamento_gpu.name # mesma versao de GPU do tempo real
instance_type = "ml.g4dn.xlarge"
initial_instance_count = 1
}
async_inference_config {
output_config {
s3_output_path = "s3://${var.bucket_lake}/saida-assincrona/"
notification_config {
success_topic = aws_sns_topic.async_sucesso.arn
error_topic = aws_sns_topic.async_erro.arn
}
}
client_config {
max_concurrent_invocations_per_instance = 4
}
}
}
resource "aws_sagemaker_endpoint" "assincrono" {
name = "${var.projeto}-cancelamento-assincrono"
endpoint_config_name = aws_sagemaker_endpoint_configuration.assincrono.name
}
# lambda_consumidor_fila.py -- le a fila, grava o payload no S3 de entrada,
# chama invoke_endpoint_async, e NAO espera o resultado.
import json
import uuid
import boto3
s3 = boto3.client("s3")
runtime = boto3.client("sagemaker-runtime")
BUCKET = "cadencia-lake"
ENDPOINT = "cadencia-cancelamento-assincrono"
def handler(event, context):
for registro in event["Records"]:
payload = json.loads(registro["body"])
chave = f"entrada-assincrona/{uuid.uuid4()}.json"
s3.put_object(Bucket=BUCKET, Key=chave, Body=json.dumps(payload).encode())
# Retorna na hora -- o retorno NAO e a pontuacao, e onde ela vai aparecer.
resposta = runtime.invoke_endpoint_async(
EndpointName=ENDPOINT,
InputLocation=f"s3://{BUCKET}/{chave}",
ContentType="application/json",
)
print(f"pedido={payload['pedido_id']} output={resposta['OutputLocation']}")
Esquecer o tópico de erro deixa item travado em silêncio
Se `error_topic` não estiver configurado, uma falha de inferência assíncrona simplesmente não notifica ninguém — o item não aparece no S3 de saída, e sem o tópico de erro não existe sinal de que algo deu errado. O Sistema de Pedidos ficaria esperando indefinidamente uma notificação que nunca chega.
Construir: lote noturno, para pontuar tudo de uma vez
Batch Transform não é um recurso que fica de pé — é uma execução que o Terraform não administra como infraestrutura permanente. O que fica de pé é o agendador e a função que dispara o job.
# lote-noturno.tf -- agenda o disparo; o job em si nao e um recurso do Terraform
resource "aws_scheduler_schedule" "lote_noturno" {
name = "${var.projeto}-lote-cancelamento-noturno"
group_name = "default"
flexible_time_window { mode = "OFF" }
schedule_expression = "cron(0 2 * * ? *)" # 2h, fora do horario de trafego interativo
target {
arn = aws_lambda_function.disparar_lote.arn
role_arn = aws_iam_role.scheduler_invoca_lambda.arn
}
}
data "aws_iam_policy_document" "lambda_dispara_transform_job" {
statement {
effect = "Allow"
actions = ["sagemaker:CreateTransformJob", "sagemaker:DescribeTransformJob"]
resources = ["arn:aws:sagemaker:${var.regiao}:${var.conta_id}:transform-job/${var.projeto}-*"]
}
}
resource "aws_iam_role_policy" "lambda_dispara_transform_job" {
role = aws_iam_role.lambda_disparar_lote.id
policy = data.aws_iam_policy_document.lambda_dispara_transform_job.json
}
# lambda_disparar_lote.py -- cria o transform job; ele processa e termina
# sozinho, sem deixar endpoint ligado depois.
import boto3
from datetime import datetime, timezone
sagemaker = boto3.client("sagemaker")
def handler(event, context):
nome_job = f"cadencia-lote-cancelamento-{datetime.now(timezone.utc):%Y%m%d}"
sagemaker.create_transform_job(
TransformJobName=nome_job,
ModelName="cadencia-cancelamento-gpu",
TransformInput={
"DataSource": {"S3DataSource": {
"S3DataType": "S3Prefix",
"S3Uri": "s3://cadencia-lake/lote-pedidos/entrada/",
}},
"ContentType": "application/json",
"SplitType": "Line",
},
TransformOutput={
"S3OutputPath": "s3://cadencia-lake/lote-pedidos/pontuacao/",
},
TransformResources={
"InstanceType": "ml.g4dn.xlarge",
"InstanceCount": 2, # paraleliza os 3.400 pedidos entre 2 instancias
},
)
print(f"job {nome_job} disparado")
O job não deixa endpoint ligado, mas cada instância cobra pelo tempo real de execução
SageMaker Training/Processing/Batch Transform cobra por segundo de instância, não por hora cheia arredondada — diferente do modelo antigo de EC2 On-Demand pré-2017. Duas instâncias por 7 minutos somam 14 minutos-instância cobrados, não duas horas. A duração curta é o que mantém o custo baixo; não existe piso de uma hora escondido aqui.
Implantar, e medir os quatro modos lado a lado
Esta é a prova que o entregável deste laboratório promete: os quatro modos medidos com o mesmo critério — custo em horas-instância pagas por mês e latência p50/p99 — para que a escolha de cada consumidor seja defensável com número, não com preferência.
# medir_quatro_modos.sh -- dispara N chamadas contra cada endpoint e registra
# os percentis de latencia; horas-instancia sao lidas do CloudWatch/Cost Explorer.
for MODO in tempo-real serverless assincrono; do
echo "--- medindo $MODO ---"
for i in $(seq 1 50); do
inicio=$(date +%s%N)
aws sagemaker-runtime invoke-endpoint \
--endpoint-name "cadencia-cancelamento-${MODO}" \
--content-type application/json \
--body '{"pedido_id":"PED-BENCH","valor_total":250.00}' \
/tmp/saida-${MODO}-${i}.json > /dev/null
fim=$(date +%s%N)
echo $(( (fim - inicio) / 1000000 )) >> /tmp/latencias-${MODO}.txt
done
echo "p50: $(sort -n /tmp/latencias-${MODO}.txt | awk 'NR==25')ms"
echo "p99: $(sort -n /tmp/latencias-${MODO}.txt | awk 'NR==50')ms"
done
# Horas-instancia pagas no mes: leia no Cost Explorer, filtrado por
# ResourceId do endpoint/job -- nao estime, confira o numero real da conta.
aws ce get-cost-and-usage --time-period Start=2026-07-01,End=2026-08-01 \
--granularity MONTHLY --metrics UsageQuantity \
--filter '{"Dimensions":{"Key":"SERVICE","Values":["Amazon SageMaker"]}}'
| Modo | Como cobra | Horas-instância pagas/mês (cenário Cadência) | Latência p50 | Latência p99 | Quando compensa |
|---|---|---|---|---|---|
| Tempo real síncrono | por hora de instância provisionada, esteja ou não respondendo | ≈ 270 h/mês (janela de 9h às 18h, scale-to-zero fora dela) | 45 ms | 118 ms | chamada rara mas bloqueante, que não pode pagar cold start |
| Serverless | por duração de invocação × memória alocada — sem hora parada | ≈ 0 h-instância equivalentes/mês (menos de 1 chamada/dia) | 210 ms (container já de pé) | 2.850 ms (com cold start) | chamada esporádica, tolerante a alguns segundos de espera ocasional |
| Assíncrono | por hora de instância enquanto a fila tem item a processar; escala a zero quando vazia | ≈ 15 h/mês (155 chamadas/dia distribuídas ao longo do dia) | 950 ms (fila + processamento) | 3.400 ms (quando escala a partir de zero) | chamada que não pode bloquear quem chamou, payload maior vindo do S3 |
| Batch Transform | só durante a janela do job, sem endpoint permanente | ≈ 7 h/mês (2 instâncias × 7 min × 30 noites, cobrado por segundo) | não se aplica por chamada — mede-se throughput | 3.400 pedidos em 7 min ≈ 8,1 pedidos/segundo | pontuar um volume grande de uma vez, sem ninguém esperando resposta individual |
Somando os quatro: cerca de 292 horas-instância pagas por mês contra as 720 do endpoint único sempre ligado — uma redução de 59%. E isso sem contar a capacidade nova: pontuar 3.400 pedidos por noite não existia antes deste desenho.
A utilização real da GPU sai de 0,04% para uma decisão explícita por modo
Antes, a pergunta "quanto da GPU ligada é realmente usado" tinha uma resposta constrangedora: 0,04%, medida na seção da arquitetura mínima. Depois, cada modo tem sua própria janela de provisionamento — o tempo real só fica ligado quando alguém pode chamá-lo, o serverless não paga hora nenhuma, o assíncrono escala com a fila, e o lote paga só os 7 minutos que realmente processa.
Quebrar de propósito: três falhas e o diagnóstico
As três parecem, à primeira vista, "o modelo ficou lento". O que separa uma da outra é ONDE a latência nasceu: no modo errado, na janela mal calibrada, ou na suposição de que scale-to-zero resolve tudo.
| Falha | Como provocar | Sintoma | Onde olhar | Correção |
|---|---|---|---|---|
| Vendedor vê a tela travar ao abrir um pedido | abra um pedido 5 minutos antes do horário de aquecimento programado do endpoint de tempo real | primeira chamada do dia demora muito mais que os 45 ms medidos | métrica de latência do endpoint de tempo real, primeira invocação do dia, comparada ao horário da chamada de aquecimento | ajustar a janela de scale-to-zero com folga sobre o horário real de uso, não sobre o horário comercial nominal |
| p99 do serverless muito acima do medido | espere um período de ociosidade maior que o normal antes de chamar de novo | algumas chamadas demoram segundos, a maioria continua rápida | contagem de invocações por minuto no CloudWatch, comparando com o intervalo entre chamadas | aumentar a frequência de chamadas de aquecimento, ou aceitar o cold start ocasional como parte do modo |
| Mensagens acumulando na fila do assíncrono sem processar | derrube a capacidade do endpoint assíncrono para 0 instâncias manualmente e gere tráfego | ApproximateBacklogSizePerInstance sobe e não desce | métrica de profundidade de fila e a política de auto scaling do endpoint assíncrono | revisar a política de auto scaling; confirmar que o modelo carrega sem erro na instância |
A pergunta que resolve as três
Antes de mexer em qualquer configuração, pergunte: a latência alta é o COLD START esperado do modo, ou é o modo errado para esse padrão de tráfego? A primeira exige calibrar janela ou concorrência; a segunda exige revisitar a tabela de requisitos e mover o consumidor para outro modo.
O endpoint da Cadência (ml.g4dn.xlarge) fica ligado 24 horas por dia e recebe as 200 chamadas diárias somando três consumidores. Cada chamada usa cerca de 180 ms de GPU. Qual é o problema estrutural que os quatro modos de servir resolvem — e que simplesmente desligar o endpoint fora do horário comercial NÃO resolveria sozinho?
Segurança: quatro caminhos, quatro superfícies
Cada modo novo abre uma superfície que o endpoint único não tinha: fila, função Lambda, bucket de entrada e saída, job com sua própria role. Nenhuma delas precisa de acesso amplo.
| Risco | Probabilidade | Impacto | Controle preventivo | Detecção | Resposta |
|---|---|---|---|---|---|
| Payload do webhook de pedido (com dado de cliente) passa por fila e S3 do modo assíncrono | média | médio | cifrar a fila SQS e o bucket S3 com KMS; IAM restrito ao prefixo usado por esse fluxo | CloudTrail em GetObject fora do prefixo esperado | revisar a policy e apurar o que foi acessado fora do escopo |
| Role da função consumidora com permissão de invocar QUALQUER endpoint do SageMaker | baixa | médio | Resource específico do endpoint no IAM, nunca sagemaker:InvokeEndpointAsync com Resource "*" | CloudTrail em InvokeEndpointAsync para endpoints fora da lista esperada | restringir a policy ao ARN exato do endpoint assíncrono |
| Job de Batch Transform lendo o bucket de pedidos sem escopo de prefixo | baixa | alto | role do job com Resource limitado aos prefixos de entrada e saída deste job específico | CloudTrail em GetObject/PutObject fora dos dois prefixos | corrigir a policy e confirmar que nenhum outro prefixo foi tocado |
| Endpoint serverless sem teto de concorrência, sujeito a custo inesperado de pico | média | médio | MaxConcurrency configurado com folga, mas nunca ilimitado; alarme de invocações acima do esperado | contagem de invocações por minuto no CloudWatch | investigar a origem do pico; ajustar MaxConcurrency conforme o padrão real |
# iam-minimo-consumidor.tf -- a role da funcao consumidora, sem "*"
data "aws_iam_policy_document" "lambda_invoca_async" {
statement {
effect = "Allow"
actions = ["sagemaker:InvokeEndpointAsync"]
resources = [
"arn:aws:sagemaker:${var.regiao}:${var.conta_id}:endpoint/${var.projeto}-cancelamento-assincrono",
]
}
statement {
effect = "Allow"
actions = ["s3:PutObject"]
resources = ["arn:aws:s3:::${var.bucket_lake}/entrada-assincrona/*"]
}
}
Role de job de lote com escopo de bucket inteiro é a superfície mais fácil de esquecer
O job de Batch Transform roda com uma role própria — diferente da role do endpoint de tempo real — e é comum copiar uma policy existente "para simplificar" e dar acesso ao bucket inteiro. Isso permite ao job ler e escrever em qualquer prefixo do lake, não só nos dois que ele realmente usa.
Observabilidade: as perguntas que o painel tem de responder
Um painel de serving tem uma função estreita: dizer se cada modo está entregando o que o consumidor dele precisa, e quanto isso está custando. Métrica que não ajuda essas duas perguntas pertence a outro painel.
| Pergunta | Métrica ou consulta | O que significa mudar | Limiar inicial |
|---|---|---|---|
| O tempo real está dentro do orçamento de latência do vendedor? | percentil de latência do endpoint de tempo real no CloudWatch | acima do limiar indica cold start fora da janela esperada, não degradação do modelo | p99 acima de 300 ms |
| O serverless está tendo cold start com mais frequência que o esperado? | contagem de invocação vs. intervalo entre chamadas | intervalo maior que o período de reciclagem do container aumenta a chance de cold start | mais de 20% das chamadas com latência acima de 1.500 ms |
| A fila do assíncrono está acumulando? | ApproximateBacklogSizePerInstance | acúmulo sustentado indica capacidade insuficiente, não pico pontual | crescendo por mais de 15 minutos seguidos |
| O job de Batch Transform termina antes do horário comercial? | duração do job comparada ao horário de início configurado | job que ainda roda às 8h atrasa o painel de retenção do dia | acima de 30 minutos de duração |
| Quantas horas-instância cada modo pagou este mês? | Cost Explorer filtrado por endpoint/job | é a métrica que prova, ou desmente, a decisão deste laboratório | acima de 320 h/mês somando os quatro modos (10% sobre o medido) |
A métrica que engana neste desenho
Latência baixa em TODOS os modos ao mesmo tempo não significa que a escolha está certa — pode significar que o tempo real está ligado além da janela necessária, "resolvendo" latência com hora paga em excesso. O painel certo cruza latência COM horas pagas, nunca olha uma sem a outra.
Escala: 200 chamadas, 2.000 chamadas, e o que passa a doer
| Volume | O que acontece | O que passa a doer | O que fazer |
|---|---|---|---|
| 200 chamadas/dia + 3.400/noite (Cadência hoje) | os quatro modos absorvem o padrão atual sem esforço | nada | nada |
| 2.000 chamadas/dia (10×) | tempo real e assíncrono escalam automaticamente; serverless pode esbarrar no teto de MaxConcurrency | throttling no modo serverless se o limite não acompanhar o crescimento | aumentar MaxConcurrency, ou mover parte do tráfego esporádico para o modo assíncrono |
| 34.000 pedidos no lote noturno (10×) | o job de Batch Transform demora mais, precisa de mais instâncias em paralelo | job pode não terminar antes do horário comercial | aumentar InstanceCount do job e medir a nova duração antes de confiar nela |
| Pico simultâneo nos quatro modos (ex.: Black Friday da construção civil) | cada modo compete pela mesma cota de conta de instâncias ml.g4dn simultâneas | a cota de instância GPU da conta vira o gargalo real, não o desenho | Service Quotas — solicitar aumento de cota ANTES do pico, não durante |
| Falha de AZ | endpoints do SageMaker distribuem instâncias entre AZ dentro da região por padrão | nada específico deste módulo, mas confirme contagem mínima de instância por variant | manter InitialInstanceCount ≥ 2 nos modos que não toleram indisponibilidade parcial |
| Consumidor novo aparece (ex.: app do lojista parceiro também quer prever cancelamento) | mais um perfil de tráfego para classificar entre os quatro modos | a decisão de modo vira recorrente, não única | reaplicar a tabela de requisitos deste módulo como checklist para o próximo consumidor |
O gargalo que só aparece com muito tráfego simultâneo
Os quatro modos, isolados, escalam bem. O que não escala automaticamente é a cota de instâncias GPU simultâneas da conta — todos os modos que usam ml.g4dn.xlarge (tempo real, assíncrono, lote) competem pela MESMA cota regional. Um pico nos três ao mesmo tempo pode esbarrar num teto que nenhum dos três, isoladamente, alcançaria.
Custo: o que este laboratório muda na fatura
A redução de horas-instância pagas é o número mais visível, mas não é o único: o modo certo também evita pagar concorrência ou memória além do que cada padrão de tráfego pede.
| Cenário | Volume | O que acontece com a fatura | Tendência | Otimização |
|---|---|---|---|---|
| Piloto | poucas centenas de chamadas, sem separar por modo ainda | endpoint único sempre ligado, sem otimização | desprezível em volume absoluto, mas 100% do custo é hora ociosa | nenhuma até validar que o modelo funciona |
| Produção pequena (Cadência hoje) | 200 chamadas/dia + 3.400/noite | ≈ 292 h-instância pagas por mês somando os quatro modos, contra 720 antes | baixa e previsível, com o tempo real dominando o total | ajustar a janela de scale-to-zero conforme o horário real de uso |
| Alta escala | 2.000 chamadas/dia + 34.000/noite | horas-instância crescem principalmente no lote (mais instâncias em paralelo) e um pouco no tempo real | linha visível, mas ainda pequena comparada a manter GPU 24h em todos os modos | revisar MaxConcurrency do serverless e paralelismo do lote antes de assumir escala linear |
| Dimensão | Cobra por | Cuidado |
|---|---|---|
| Instância do tempo real | hora de instância provisionada | pagar 24h quando o uso real é só o horário comercial é o erro mais caro deste laboratório |
| Invocação do serverless | duração × memória alocada (GB-segundo) | sem GPU — confirme que o modelo roda dentro do orçamento de latência em CPU antes de prometer SLA |
| Instância do assíncrono | hora de instância enquanto a fila tem item, escala a zero quando vazia | payload grande no S3 também soma custo de armazenamento e requisição, separado da instância |
| Job de Batch Transform | hora de instância só durante a execução do job | mais instâncias em paralelo reduzem a duração e o custo total junto — não há hora mínima por instância |
O número que mais importa não é a redução — é a capacidade nova
59% menos horas-instância pagas por mês é um bom resultado. Mas o que a equipe de retenção realmente ganhou foi pontuar 3.400 pedidos abertos toda noite, coisa que simplesmente não existia quando o único caminho disponível era chamar o endpoint de tempo real pedido por pedido.
Well-Architected nos seis pilares
| Pilar | Situação ao fim deste laboratório | Risco que fica | Melhoria | Prioridade |
|---|---|---|---|---|
| Excelência operacional | quatro modos com donos e métricas claros, roteamento decidido por tabela de requisitos | padrão de tráfego de um consumidor pode mudar sem ninguém notar e reconfigurar o modo | revisão trimestral da tabela de requisitos por consumidor, como processo declarado | média |
| Segurança | cada modo com role própria e escopo por prefixo/recurso, sem Resource "*" | payload de dado de cliente passando por mais superfícies (fila, S3 intermediário) que antes | avaliar cifra em trânsito e repouso consistente nos quatro caminhos, não só no assíncrono | média |
| Confiabilidade | falha de um modo não derruba os outros três — são endpoints/jobs independentes | cota de instância GPU compartilhada entre tempo real, assíncrono e lote é ponto único de contenção | monitorar Service Quotas de instância GPU e solicitar aumento proativo antes de picos conhecidos | alta |
| Eficiência de performance | cada modo dimensionado para o padrão de tráfego real do consumidor dele | janela de scale-to-zero do tempo real pode ficar desatualizada se o horário de uso mudar | alarme comparando primeira chamada do dia com o horário de aquecimento configurado | média |
| Otimização de custos | ≈ 59% menos horas-instância pagas por mês, medido nesta seção | cota de MaxConcurrency do serverless configurada com folga pode custar mais que o necessário em pico | revisar MaxConcurrency com base no p99 de invocações simultâneas observado, não em estimativa | média |
| Sustentabilidade | GPU ligada só quando há trabalho real para fazer, nos quatro modos | chamadas de aquecimento programadas gastam ciclo de GPU mesmo em dias sem uso real do endpoint | condicionar o aquecimento a sinal de que o horário comercial realmente terá tráfego (ex.: feriado) | baixa |
Evolução em níveis: o que muda, e o que passa a doer
A terceira arquitetura não é um desenho: é a resposta a QUANDO trocar de desenho. Cada nível resolve um risco deste módulo e compra outro — até o ponto em que a mesma decisão de modo se repete sobre um modelo que já não é mais um classificador tabular, e sim um LLM.
Um endpoint de tempo real atende qualquer chamador, 24 horas por dia — é onde a Cadência estava até este laboratório, e é aceitável enquanto o volume é baixo o suficiente para ninguém notar o custo.Tempo real com janela reduzida, serverless em CPU, assíncrono com fila, Batch Transform para lote — cada consumidor no modo que casa com o requisito dele.O consumidor do endpoint de tempo real ganha degradação graciosa: se o modelo não responder dentro do orçamento de latência, o vendedor vê um valor padrão em vez de a tela travar.Escolher o modo certo não garante que ninguém deixe uma instância provisionada além do necessário dentro do próprio modo certo — o L80 audita a fatura de ML inteira, não só o serving.Servir rápido e barato não prova que o modelo está evitando cancelamento de verdade — o L78 liga a pontuação a uma métrica de negócio (cancelamento evitado), não só a latência e custo de serving.Quando o "modelo" servido deixa de ser um classificador e passa a ser um LLM via Bedrock, a MESMA pergunta continua — só que entre invocação on-demand, provisioned throughput e inferência em lote do Bedrock, não entre os quatro modos do SageMaker (é o tema do L89, custo e latência de GenAI).A ordem não é negociável, e o motivo é concreto
Ligar fallback (nível 3) antes de separar os quatro modos (nível 2) esconderia o problema real: um fallback bem desenhado sobre um endpoint único ainda pagaria GPU ociosa 24h — só que sem ninguém reclamando de latência, porque o fallback disfarça o sintoma sem tocar a causa.
Onde IA entra nesta arquitetura, e onde não entra
O modelo que este laboratório serve já É a parte de IA do sistema — previsão de cancelamento treinada no L73. A pergunta desta seção é diferente: vale a pena usar IA para decidir QUAL dos quatro modos usar em cada chamada? A resposta, para a escala da Cadência hoje, é não.
O padrão de tráfego de cada consumidor é conhecido e estável — o painel de atendimento sempre vai bloquear e ser raro, o Sistema de Pedidos sempre vai precisar de resposta não-bloqueante, o lote sempre vai rodar de noite. Uma tabela de requisitos, revisada quando um consumidor novo aparece, resolve isso de forma auditável e sem custo de inferência extra.
| Pergunta | Resposta honesta para este módulo |
|---|---|
| Qual problema a IA resolveria aqui? | nenhum — a escolha de modo depende de propriedades do CONSUMIDOR (bloqueia ou não, frequência, tamanho de payload), que são conhecidas de antemão, não algo que precise ser inferido de dado histórico |
| Por que uma tabela de decisão estática basta? | porque os quatro atributos que decidem o modo são estáveis por semanas ou meses — engenharia de sistemas resolve isso, não aprendizado de máquina |
| Onde IA entraria de fato neste tema? | em decidir DINAMICAMENTE o modo a partir de tráfego observado em tempo real — por exemplo, prever um pico e pré-aquecer o endpoint de tempo real antes dele acontecer — mas isso só compensaria numa escala muito maior que a da Cadência hoje |
| Qual o risco de automatizar a escolha de modo com um classificador? | trocar uma decisão auditável (a tabela de requisitos, revisável em PR) por uma previsão que pode errar justamente no caso raro e crítico — o vendedor esperando a tela travar é o pior lugar para uma previsão de roteamento errada |
O uso de IA que parece atraente e é armadilha aqui
Treinar um modelo para prever, a cada chamada, qual dos quatro modos usar, trocaria uma decisão que qualquer engenheiro audita numa tabela de duas colunas por uma fronteira de decisão que ninguém explica sem abrir o modelo — para um problema que uma regra estática já resolve com precisão total, porque as propriedades que decidem o modo não mudam chamada a chamada.
Anti-padrões deste laboratório
| Anti-padrão | Por que alguém faz | Por que é problema | Sintoma em produção | Forma correta | Quando é aceitável |
|---|---|---|---|---|---|
| Usar tempo real para tudo, mesmo o que pode esperar | é o exemplo que aparece em todo tutorial de SageMaker, e funciona sem pensar em fila, S3 ou job | cobra por hora ligada, esteja ou não respondendo, e não ganha nada em troca para chamadas raras ou tolerantes a espera | GPU ociosa 99%+ do tempo, fatura dominada por horas paradas | escolher o modo pelo padrão de tráfego real — frequência, se bloqueia quem chama, tamanho do payload — não pelo que é mais fácil de configurar primeiro | protótipo de um dia, ou volume real ainda desconhecido, quando você só precisa de UM modo para validar que o modelo funciona |
| Colocar tudo em Batch Transform, mesmo o que precisa de resposta imediata | "total do lote dividido pelo total de chamadas" costuma dar uma conta atraente de custo por inferência | ninguém recebe pontuação em tempo real; o vendedor esperaria até a próxima janela do job, que pode ser horas depois | usuário reclamando que a pontuação está sempre desatualizada | reservar lote para o que genuinamente não tem usuário esperando na tela | todo consumo é offline e tolera a janela do job, como um relatório noturno |
| Tratar Serverless Inference como "tempo real mais barato" | o nome sugere que só trocou o modelo de cobrança, mantendo o resto igual | esconde dois limites reais: cold start ocasional e ausência de suporte a GPU — a troca de modo às vezes exige trocar a versão do modelo também | latência p99 explode sem explicação aparente, ou deploy falha porque a instância pedida não é suportada | medir cold start ANTES de prometer SLA, e confirmar que o modelo roda em CPU dentro do orçamento de latência | tráfego intermitente com folga de latência suficiente para absorver cold start ocasional |
| Achar que scale-to-zero no tempo real resolve tudo sozinho | a funcionalidade é recente e parece a resposta simples para "GPU ociosa" | ainda paga cold start ao acordar, ainda bloqueia quem chama enquanto processa, e não resolve o que pede lote ou não-bloqueio | latência inconsistente logo após período ocioso, mesmo com scale-to-zero configurado | escolher o modo pelo padrão de tráfego primeiro; scale-to-zero é uma otimização DENTRO do tempo real, não substituto dos outros três | tráfego de fato só existe dentro de uma janela previsível e sempre bloqueante |
Quando algo não funciona
| Sintoma | Causa provável | Como investigar | Onde olhar | Correção |
|---|---|---|---|---|
| Latência p99 do serverless muito acima do esperado | cold start acontecendo com mais frequência do que o previsto | comparar intervalo entre chamadas com o tempo de ociosidade que dispara reciclagem do container | métrica de latência por invocação no CloudWatch, filtrando os picos | aumentar a frequência de aquecimento, ou mover o consumidor para tempo real se o tráfego for mais previsível do que se pensava |
| Mensagens acumulando na fila do assíncrono sem processar | endpoint assíncrono sem capacidade de auto scaling configurada, ou instância presa em erro | checar profundidade de fila e a política de auto scaling do endpoint assíncrono | ApproximateBacklogSizePerInstance no CloudWatch | revisar a política de auto scaling; confirmar que o modelo carrega sem erro na instância |
| Job de Batch Transform demora muito mais que o medido | volume de pedidos abertos cresceu sem aumentar o número de instâncias do job | comparar duração do job ao longo das últimas semanas com o volume de pedidos abertos | duração do transform job nos logs do CloudWatch | escalar InstanceCount proporcionalmente ao volume, não fixar um número |
| Vendedor reclama de tela travando ao abrir pedido | endpoint de tempo real escalou a zero fora do horário esperado, e a primeira chamada paga cold start | comparar horário da reclamação com a janela configurada de scale-to-zero | métrica de invocação e latência do endpoint de tempo real, primeira chamada do dia | ajustar a janela de scale-to-zero com folga sobre o horário real de uso |
A pergunta que resolve metade destes casos
Antes de mexer em qualquer configuração, pergunte: a latência alta é o comportamento ESPERADO do modo (cold start do serverless, tempo de fila do assíncrono), ou é sinal de que o modo escolhido não serve mais para esse consumidor? A primeira exige calibrar; a segunda exige revisitar a tabela de requisitos.
Limpeza: o que fica ligado depois do "terminei"
Este laboratório cria quatro superfícies de custo diferentes, e cada uma tem uma forma própria de continuar cobrando depois que você fecha o terminal.
#!/usr/bin/env bash
# limpar.sh -- os quatro modos, cada um com seu jeito de continuar cobrando
set -euo pipefail
PROJETO="${PROJETO:?defina PROJETO}"
# 1. Endpoints (tempo real, serverless, assincrono) -- cada um deixado no ar
# continua cobrando hora de instancia ou concorrencia reservada.
for MODO in tempo-real serverless assincrono; do
aws sagemaker delete-endpoint --endpoint-name "${PROJETO}-cancelamento-${MODO}" || true
aws sagemaker delete-endpoint-config --endpoint-config-name "${PROJETO}-cancelamento-${MODO}" || true
done
# 2. Job de Batch Transform ja termina sozinho -- nao ha o que apagar aqui,
# mas confirme que nao ha um job preso em InProgress cobrando instancia.
aws sagemaker list-transform-jobs --status-equals InProgress \
--query "TransformJobSummaries[?contains(TransformJobName, '${PROJETO}')]"
# 3. Fila, topicos SNS e agendadores -- centavos, mas o Terraform destroy cobre.
terraform destroy -auto-approve
# 4. O DADO gravado (entrada/saida assincrona, saida do lote) NAO sai no destroy.
aws s3 rm "s3://cadencia-lake/entrada-assincrona/" --recursive
aws s3 rm "s3://cadencia-lake/saida-assincrona/" --recursive
aws s3 rm "s3://cadencia-lake/lote-pedidos/" --recursive
# 5. Prova final: nada com o nome do projeto de pe fora do esperado.
aws resourcegroupstaggingapi get-resources \
--tag-filters Key=Projeto,Values="${PROJETO}" \
--query "ResourceTagMappingList[].ResourceARN" --output table
| Recurso | Sai no destroy? | Cobra parado? | Por que fica |
|---|---|---|---|
| Endpoint de tempo real (aws_sagemaker_endpoint) | não automaticamente — precisa de delete-endpoint explícito | sim, por hora de instância | o Terraform administra o RECURSO de configuração; apagar o endpoint em si costuma ser um passo manual antes do destroy |
| Endpoint serverless | mesmo caso do tempo real | não entre chamadas (sem hora parada), mas cobra por invocação enquanto existir | endpoint esquecido continua aceitando chamadas de teste, e cada uma cobra |
| Endpoint assíncrono, fila SQS e tópicos SNS | sim, se em Terraform | centavos | fila ou tópico criado à mão no console não aparece no estado do Terraform |
| Dado em entrada-assincrona/, saida-assincrona/ e lote-pedidos/ no S3 | não — não pertence ao Terraform, é dado gravado pelos jobs e pelo endpoint | sim, GB-mês | decisão editorial: manter para auditoria por um período, ou apagar — este módulo não decide isso por você |
| Job de Batch Transform em si | termina sozinho quando o processamento acaba | não depois de terminar | mas um job preso em InProgress por erro continua cobrando instância até alguém notar |
Apagar o endpoint não apaga a versão do modelo em CPU criada só para o serverless
O artefato compilado para CPU continua no S3 e o `aws_sagemaker_model` associado continua registrado mesmo depois de remover o endpoint serverless — é dado pequeno, mas some da lista de "recursos ativos" sem sumir da lista de "coisas que alguém precisa lembrar que existem".
Resumo: problema, peça e motivo
| Problema | Peça | Por que ela, e não outra |
|---|---|---|
| GPU ligada 24h para atender chamada rara e bloqueante | endpoint de tempo real com janela de disponibilidade reduzida e scale-to-zero fora dela | mantém a latência que o vendedor precisa, sem pagar hora fora do horário real de uso |
| Chamada esporádica do time de dados no mesmo endpoint caro | endpoint serverless sobre uma versão do modelo compilada para CPU | zero hora paga ociosa — mas exige aceitar a ausência de GPU nesse caminho |
| Webhook de pagamento bloqueado esperando o modelo | endpoint assíncrono com fila, S3 de entrada/saída e notificação SNS | desacopla a resposta do modelo da resposta do próprio evento de pagamento |
| 3.400 pedidos abertos nunca pontuados de uma vez | job de Batch Transform disparado por agendador, às 2h | processa em paralelo entre instâncias e termina sozinho, sem endpoint permanente |
| Custo de serving invisível, sem comparação entre modos | medição de horas-instância pagas e latência p50/p99, lado a lado | transforma "parece caro" em uma tabela com número — a prova que o entregável promete |
| Escolha de modo por consumidor sujeita a ficar desatualizada | não resolvido neste módulo | risco residual aceito e documentado; revisão trimestral é recomendação, não automação |
| Falha | O que a protege | O que ela NÃO protege |
|---|---|---|
| Vendedor esperando resposta do modelo travar a tela | endpoint de tempo real com janela dimensionada para o horário real de uso | chamada fora da janela configurada, que ainda paga cold start |
| Webhook de pagamento bloqueado pelo modelo | fila e endpoint assíncrono, desacoplando as duas respostas | falha silenciosa se o tópico SNS de erro não estiver configurado |
| Custo dominado por hora ociosa | quatro modos dimensionados por padrão de tráfego real, medidos com número | configuração esquecida dentro de um modo já certo — é o assunto do L80 |
| 3.400 pedidos nunca pontuados juntos | Batch Transform paralelizado, disparado fora do horário de pico | volume crescendo além do que o número atual de instâncias processa na janela |
| Escolha de modo errada para um consumidor novo | tabela de requisitos como checklist reutilizável | ninguém aplicar a tabela quando um consumidor novo aparece — é processo, não automação |
- O modelo de previsão de cancelamento já está treinado e versionado, resultado do L73.
- Cada consumidor (painel de atendimento, script de análise, Sistema de Pedidos, painel de retenção) tem um padrão de tráfego diferente.
- A arquitetura mínima força os três consumidores interativos contra o mesmo endpoint de GPU sempre ligado.
- A arquitetura de produção roteia cada consumidor pelo modo que casa com o requisito dele.
- Tempo real síncrono atende o painel de atendimento, com janela reduzida e scale-to-zero fora do expediente.
- Serverless atende o script de análise, sobre uma versão do modelo compilada para CPU.
- Assíncrono atende o Sistema de Pedidos, com fila absorvendo o pico sem bloquear o webhook.
- Batch Transform atende o painel de retenção, pontuando 3.400 pedidos de uma vez, à noite.
- A seção de implantação mede os quatro modos lado a lado: horas-instância pagas e latência p50/p99.
- O resultado medido é uma redução de 59% nas horas-instância pagas, mais a capacidade nova de pontuar o lote inteiro.
Desafio — sem roteiro
O requisito
Um padrão de tráfego novo chegou, diferente dos quatro já cobertos: picos previsíveis (ex: todo dia às 9h um lote grande) intercalados com longos períodos sem nenhuma chamada. Usando a mesma árvore de decisão do laboratório, escolha e implemente o modo correto — e justifique por que os outros três NÃO servem para esse padrão.
Critério de aceite — executável, não "verifique se funciona"
O texto de justificativa cita explicitamente por que endpoint em tempo real sempre ligado seria desperdício (idle sem tráfego) e por que batch simples não serve (latência do lote não cabe na janela de 9h) — e a implementação escolhida processa o pico das 9h dentro do tempo aceitável.
- Dica 1: A pergunta que decide entre os quatro modos não é "quão grande é o modelo", é "qual é a FORMA da demanda no tempo" — pico previsível e vazio no resto é o caso clássico de escalar sob demanda, não de manter always-on.
- Dica 2: Se a escolha for serverless/assíncrono, o cold start do primeiro request do pico das 9h pode ser o gargalo — meça especificamente a LATÊNCIA DO PRIMEIRO request do lote, não só a média.
- Dica 3: Registre por escrito o custo de cada uma das 4 opções para ESSE padrão específico de tráfego — é o que transforma "escolhi X" em uma decisão de engenharia, não um palpite.
Lembrete de limpeza
Todo recurso que este desafio criar entra no mesmo `terraform destroy` do laboratório — nada fica para trás cobrando sozinho.
Perguntas frequentes
❓ Por que não simplesmente desligar o endpoint fora do horário comercial?
❓ Serverless Inference aceita instância com GPU?
❓ O modo assíncrono bloqueia quem chama o endpoint?
❓ Batch Transform mantém um endpoint ligado depois que o job termina?
❓ Preciso reescrever o modelo para cada um dos quatro modos?
❓ Qual modo escolher quando a latência não importa, mas o payload é grande?
❓ Scale-to-zero no endpoint de tempo real substitui os outros três modos?
❓ O Model Registry do L75 se aplica aos quatro modos, ou só ao tempo real?
Fixando
O time de dados quer mover as chamadas esporádicas do script de análise (menos de uma por dia) para Serverless Inference, mas o modelo de previsão de cancelamento foi treinado para se beneficiar de GPU. O que a arquitetura de produção deste laboratório faz para resolver essa contradição?
Antes deste laboratório, ninguém pontuava os 3.400 pedidos abertos de uma vez — só chamadas individuais existiam. Depois, o Batch Transform processa os 3.400 pedidos em cerca de 7 minutos, uma vez por noite. Por que essa capacidade só apareceu depois de separar os quatro modos, e não já existia com o endpoint único de tempo real?
Conhecimentos, próximo módulo e documentação
| Item | Conteúdo |
|---|---|
| Conhecimentos anteriores necessários | L73 (SageMaker AI, treino rastreável, experimento reproduzível) — o artefato do modelo já existe e é o que este módulo serve |
| Conhecimentos adquiridos | os quatro modos de inferência do SageMaker (tempo real, serverless, assíncrono, Batch Transform); quando cada um se paga em custo e latência; a restrição de GPU do serverless; a diferença entre "endpoint" e "job" que o Batch Transform representa |
| Limitação que fica | a escolha de modo por consumidor é decidida uma vez, na tabela de requisitos — se o padrão de tráfego de um consumidor mudar, nada detecta isso automaticamente |
| Próximo exemplo recomendado | L79 — Consumir o modelo do .NET com fallback. Reutiliza o endpoint de tempo real deste módulo e resolve o próximo risco: o que acontece quando o timeout de 300 ms estoura de verdade |
| Também habilitado por este módulo | L80 (custo de ML: onde o dinheiro vaza) depende da medição feita aqui — sem saber quanto cada modo paga, não há linha de base para auditar desperdício. L78 (avaliação honesta) também depende deste módulo para ligar a pontuação servida a uma métrica de negócio real |
| Data da última validação técnica | 8 de agosto de 2026 |
Documentação oficial consultada: SageMaker AI — Real-time inference, Serverless Inference, Asynchronous Inference e Batch Transform, incluindo os limites de memória e concorrência do modo serverless e a ausência de suporte a GPU nesse modo; SageMaker endpoint auto scaling e o recurso de escalar instâncias de tempo real a zero; o resource `aws_sagemaker_endpoint_configuration` no provider Terraform da AWS. Nenhum valor em dólar aparece neste módulo por decisão: os números de horas-instância e latência são medições do cenário da Cadência, e custo em moeda muda por região e por acordo de conta — confira o AWS Pricing Calculator para o valor atual.
O que não foi verificado, e você deve conferir na sua conta
O suporte a `managed_instance_scaling` com `min_instance_count = 0` em endpoints de tempo real depende da versão do provider Terraform e pode não estar disponível em toda região — confirme antes de assumir que a janela reduzida funciona como descrito. Os valores fixos de `memory_size_in_mb` do serverless (1024 a 6144) também merecem checagem na documentação atual antes de escrever Terraform de produção em cima deles. Os volumes da Cadência — 200 chamadas/dia, 3.400 pedidos/noite — são os do cenário de exemplo; meça os seus antes de copiar um limiar de alarme.
Terminou de ler?
Marcar como concluído registra o XP, mantém sua sequência e coloca 3 cartas deste módulo na fila de revisão espaçada.
Próximos passos sugeridos
Temas deste módulo
Discussão
Carregando comentários…