Introdução
Um ticket não deve ser fechado enquanto sua nota de resolução não estiver salva. Você usará um lote preparado do D1 para manter essas gravações juntas e, em seguida, enviará um marcador da API de Sessions entre as solicitações para que as leituras posteriores possam observar o trabalho já confirmado.
Este laboratório diferencia a reversão atômica da consistência sequencial de sessões. Ele usa um banco de dados e um Worker independentes e não exige a ativação de réplicas de leitura nem a reprodução de uma condição de corrida de replicação.
Use sua própria conta de aprendizado e uma VM nova. A configuração prepara o Node.js 22.22.0 e executa npm install para instalar o Wrangler 4.131.1 localmente no projeto e quaisquer dependências de avaliação em /home/labex/project/ticket-database. As versões das dependências diretas estão fixadas; a instalação cria seu próprio lockfile. Nenhum login na nuvem nem trabalho de banco de dados avaliado é executado durante a configuração. Em uma máquina pessoal, instale a mesma versão do Wrangler com npm install --save-dev wrangler@4.131.1 no seu projeto.
Este exercício usa registros sintéticos pequenos dentro das franquias gratuitas do D1. O uso existente da conta conta para essas franquias. Nenhum domínio comprado é necessário. Mantenha esta VM até verificar a exclusão dos recursos e o logout.
Autorize esta VM e selecione a conta
Nesta etapa, você conecta este terminal novo à sua própria conta de aprendizado. Apenas fazer login no Dashboard não autoriza a VM. A permissão do D1 permite criar, alterar e excluir bancos de dados; a permissão de Workers permite fazer o deploy, e a permissão de KV permite que o Wrangler mantenha o inventário de limpeza. Revise a página de consentimento real, incluindo o acesso em segundo plano (Background Access), antes de autorizar.
Abra o projeto preparado e verifique a versão fixada da CLI:
cd /home/labex/project/ticket-database
npx wrangler --version
A saída esperada é 4.131.1. Inicie a autorização por dispositivo; --device exibe um código para o navegador, e --browser=false deixa a escolha do navegador com você:
npx wrangler login --device --browser=false --scopes account:read user:read d1:write workers_scripts:write workers_kv:write
Abra no navegador a URL exibida, informe o código atual, confirme sua conta de aprendizado e as permissões e autorize. Aguarde o terminal confirmar o sucesso. Nunca cole senhas ou tokens em arquivos do projeto.
npx wrangler whoami --json
Verifique loggedIn: true e leia o name e o id da conta, mesmo quando apenas uma conta for listada. Copie o ID pretendido para a configuração abaixo. A variável de shell a seguir usa 6 bytes aleatórios (12 caracteres hexadecimais) para evitar colisões com outros estudantes. Um here-document grava o JSON entre as linhas JSON; $RUN é expandido dentro dele.
A barra invertida antes de $schema mantém essa chave JSON literal; $RUN continua sendo expandido para o nome único desta execução.
RUN=labex-c04-d05-$(openssl rand -hex 6)
cat > wrangler.jsonc <<JSON
{
"\$schema": "./node_modules/wrangler/config-schema.json",
"name": "$RUN",
"account_id": "YOUR_ACCOUNT_ID",
"main": "src/index.js",
"compatibility_date": "2026-09-15",
"workers_dev": true,
"preview_urls": false
}
JSON
Substitua YOUR_ACCOUNT_ID antes de executar o bloco. Mantenha este terminal aberto para que RUN continue disponível. name identifica esta execução; account_id seleciona a conta usada nas operações na nuvem. O arquivo é um JSON comum, que também é válido como JSONC. Escrevê-lo não faz o deploy de nenhum Worker.
Prepare os tickets e as notas de resolução
Nesta etapa, você prepara duas tabelas relacionadas. Fechar um ticket também deve salvar sua nota de resolução. Se apenas uma gravação for bem-sucedida, a equipe poderá ver um ticket fechado sem explicação. A configuração fornece o esquema e o roteador HTTP; você implementará as operações SQL relacionadas.
Crie um banco de dados descartável na nuvem. --binding DB fornece ao código da aplicação um nome curto, --update-config registra o nome real e o UUID em wrangler.jsonc, e --use-remote=false mantém o desenvolvimento local:
npx wrangler d1 create "$RUN-db" --binding DB --update-config --use-remote=false
Leia o nome e o ID criados e inspecione o binding salvo:
cat wrangler.jsonc
A entrada DB deve identificar o banco de dados desta execução. Um binding é uma conexão configurada entre o código e um recurso. O UUID identifica o banco de dados na nuvem, enquanto --local usa um banco de dados SQLite separado nesta VM. Sempre inclua --local ou --remote nos comandos SQL.
cat schema.sql
npx wrangler d1 execute DB --local --file schema.sql
npx wrangler d1 execute DB --remote --file schema.sql
O ticket 1 está aberto e não tem resolução. O ticket 2 está fechado e é proprietário do evento de resolução 1. Esse ID de evento já ocupado fornece um caso de falha controlado: inserir outro evento 1 deve violar a chave primária.
Mantenha as gravações atômicas e as leituras sequenciais
Nesta etapa, você resolve dois problemas de consistência separados. Atomicidade significa que as duas gravações relacionadas são bem-sucedidas ou nenhuma delas é. batch() do D1 executa instruções preparadas como uma transação: uma falha reverte todo o lote. Duas gravações aguardadas separadamente não oferecem essa garantia.
Uma sessão acompanha o estado do banco de dados observado por uma sequência de consultas. O roteador fornecido chama env.DB.withSession(...), começando em first-primary quando o cliente não tem um marcador. Ele envia getBookmark() de volta no cabeçalho x-d1-bookmark. Uma solicitação posterior pode enviar esse marcador para continuar a partir de, no mínimo, esse estado do banco de dados. Isso é consistência sequencial, não uma transação de tudo ou nada entre solicitações HTTP.
Implemente as duas funções usando a sessão recebida pelo roteador:
cat > src/store.js <<'JS'
export async function closeTicket(session, id, eventId, note) {
await session.batch([
session.prepare("UPDATE tickets SET status = 'closed' WHERE id = ?").bind(id),
session.prepare('INSERT INTO resolutions(event_id, ticket_id, note) VALUES (?, ?, ?)').bind(eventId, id, note)
]);
}
export async function readTicket(session, id) {
const ticket = await session.prepare('SELECT id, subject, status FROM tickets WHERE id = ?').bind(id).first();
if (!ticket) return null;
const { results } = await session.prepare('SELECT event_id, note FROM resolutions WHERE ticket_id = ? ORDER BY event_id').bind(id).all();
return { ...ticket, resolutions: results };
}
JS
Leia src/index.js para localizar withSession, o cabeçalho do marcador recebido e o marcador retornado. Todas as operações de banco de dados da solicitação usam essa sessão. Um marcador é uma posição opaca: envie-o de volta sem alterações, em vez de tentar analisá-lo ou inventá-lo.
cat src/index.js
npx wrangler dev --ip 0.0.0.0 > dev.log 2>&1 &
cat dev.log
Aguarde a mensagem informando que o serviço local está escutando. A simulação local pode testar a reversão do lote, mas não demonstra a replicação remota real nem um marcador da nuvem.
Observe a reversão antes de fechar com sucesso
Nesta etapa, você envia deliberadamente o ID de evento já ocupado. A primeira instrução do lote tenta fechar o ticket 1, mas a segunda falha. Leia o resultado após a falha:
curl -i http://localhost:8787/tickets/1/close -H 'Content-Type: application/json' -d '{"event_id":1,"note":"Must roll back"}'
curl -i http://localhost:8787/tickets/1
Espere 409 event_conflict, seguido do ticket 1 ainda com status open e um array resolutions vazio. Um 409 sozinho não é suficiente: a leitura seguinte comprova que nenhuma atualização parcial permaneceu.
Agora use o ID de evento não utilizado 2:
curl -i http://localhost:8787/tickets/1/close -H 'Content-Type: application/json' -d '{"event_id":2,"note":"Access restored"}'
curl -i http://localhost:8787/tickets/1
Espere HTTP 200, o ticket 1 com status closed e o evento de resolução 2 com a nota Access restored. Os dois registros agora estão de acordo. Não redefina os dados locais nem altere o banco de dados remoto para fabricar um atraso de replicação.
Continue uma sessão remota usando o marcador
Nesta etapa, você executa o mesmo lote no D1 e transporta um marcador real entre as solicitações. O estado inicial do ambiente remoto ainda está preservado.
npx wrangler deploy
Copie a URL real do deploy. Primeiro, repita o lote com falha e confirme a reversão:
URL='YOUR_DEPLOYED_HTTPS_URL'
curl -i "$URL/tickets/1/close" -H 'Content-Type: application/json' -d '{"event_id":1,"note":"Must roll back"}'
curl -i "$URL/tickets/1"
Espere 409 e, em seguida, um ticket aberto sem resoluções. Se o deploy ainda estiver sendo propagado, repita a leitura por até um minuto; não confunda uma página de erro da plataforma com o contrato JSON da aplicação.
Envie o fechamento bem-sucedido:
curl -i "$URL/tickets/1/close" -H 'Content-Type: application/json' -d '{"event_id":2,"note":"Access restored"}'
Espere o ticket fechado e sua nota. Copie o cabeçalho de resposta x-d1-bookmark não vazio, sem espaços adicionais, para a variável a seguir:
BOOKMARK='YOUR_RESPONSE_BOOKMARK'
curl -i "$URL/tickets/1" -H "x-d1-bookmark: $BOOKMARK"
A solicitação seguinte deve observar o ticket fechado e o evento de resolução 2. Um marcador restringe o quão antiga uma leitura pode ser; ele não é um token de autenticação. Esse fluxo funciona sem exigir uma leitura obsoleta nem ativar a replicação de leitura. Estamos verificando o contrato da sessão, não afirmando que ocorreu uma condição de corrida entre réplicas.
Abra o Worker exato no Dashboard e confirme que o binding DB aponta para o banco de dados desta execução. Conclua a verificação funcional antes da limpeza.

Este exemplo mostra o vínculo DB do Worker apontando para seu banco D1. O prefixo gerado identifica esta execução de exemplo; seus nomes serão diferentes. A captura confirma apenas o vínculo. As verificações HTTP acima comprovam a reversão atômica, a atualização bem-sucedida e a continuação da leitura com o marcador.
Exclua os recursos descartáveis
Nesta etapa, você remove apenas os recursos deste laboratório enquanto a VM ainda está autorizada. Conclua primeiro todas as verificações funcionais. Mantenha a configuração até concluir a verificação da exclusão.
npx wrangler delete
Confirme que apenas o nome do Worker desta execução aparece na configuração.
npx wrangler d1 delete DB
Inspecione o prompt e confirme que se trata apenas do banco de dados desta execução. Em seguida, liste os bancos de dados:
npx wrangler d1 list --json
O nome e o UUID do banco de dados registrados por você devem estar ausentes em uma resposta bem-sucedida. Outros recursos podem permanecer. Um erro de autenticação ou de rede é inconclusivo: resolva o acesso e repita a leitura antes de continuar. Execute a verificação desta etapa enquanto ainda estiver conectado.
Pare também o processo de desenvolvimento local. Liste os jobs e encerre somente o job wrangler dev que você iniciou (substitua %1 se o número do job for diferente):
jobs
kill %1
Encerre a autorização desta VM
Nesta etapa, encerre a autorização somente depois que a verificação independente da exclusão for aprovada. O logout remove a autorização do Wrangler armazenada nesta VM; apenas fechar uma VM não faz a limpeza na nuvem.
npx wrangler logout
npx wrangler whoami --json
Espere loggedIn: false. Essa consulta sem autenticação pode terminar com código diferente de zero; isso só é esperado quando a resposta estruturada informa explicitamente que você está desconectado. Conclua a verificação e feche o ambiente do laboratório.
Resumo
Você praticou como manter consistentes as atualizações relacionadas de tickets. Verificou resultados observáveis do banco de dados, manteve explícitos a conta selecionada e o estado local e removeu os recursos descartáveis antes de fazer logout.



