Mantenga coherentes las actualizaciones de tickets relacionados

CloudflareBeginner
Practicar Ahora

Introducción

Un ticket no debe cerrarse hasta que se guarde su nota de resolución. Utilizará un lote preparado de D1 para mantener juntas ambas escrituras y, después, llevará un marcador de la API de Sessions entre las solicitudes para que las lecturas posteriores puedan observar el trabajo confirmado anteriormente.

Este laboratorio distingue entre la reversión atómica y la coherencia secuencial de una sesión. Utiliza una única base de datos y un único Worker independientes, y no requiere habilitar réplicas de lectura ni reproducir una condición de carrera de replicación.

Utilice su propia cuenta de aprendizaje y una VM nueva. La configuración prepara Node.js 22.22.0 y después ejecuta npm install para instalar Wrangler 4.131.1 localmente en el proyecto y cualquier dependencia de evaluación en /home/labex/project/ticket-database. Las versiones de las dependencias directas están fijadas; la instalación crea su propio archivo de bloqueo. Durante la configuración no se inicia ninguna sesión en la nube ni se realizan operaciones evaluadas en la base de datos. En una máquina personal, instale la misma versión de Wrangler con npm install --save-dev wrangler@4.131.1 en su proyecto.

Este ejercicio utiliza registros sintéticos pequeños dentro de las asignaciones gratuitas de D1. El uso existente de la cuenta cuenta para esas asignaciones. No necesita un dominio comprado. Conserve esta VM hasta comprobar tanto la eliminación de los recursos como el cierre de sesión.

Autorice esta VM y seleccione la cuenta

En este paso, conectará este terminal nuevo a su propia cuenta de aprendizaje. Iniciar sesión en el Dashboard por sí solo no autoriza la VM. El permiso de D1 permite crear, modificar mediante SQL y eliminar bases de datos; el permiso de Workers permite realizar implementaciones, y el permiso de KV permite que Wrangler mantenga un inventario para la limpieza. Revise la página de consentimiento real, incluido Background Access, antes de autorizar.

Abra el proyecto preparado e inspeccione la CLI cuya versión está fijada:

cd /home/labex/project/ticket-database
npx wrangler --version

Debe aparecer 4.131.1. Inicie la autorización mediante dispositivo; --device muestra un código para el navegador y --browser=false le permite decidir si desea abrir el navegador:

npx wrangler login --device --browser=false --scopes account:read user:read d1:write workers_scripts:write workers_kv:write

Abra en el navegador la URL que se muestra, introduzca el código actual, confirme su cuenta de aprendizaje y los permisos, y autorice. Espere a que el terminal confirme que la operación se realizó correctamente. Nunca pegue contraseñas ni tokens en archivos del proyecto.

npx wrangler whoami --json

Compruebe loggedIn: true y lea name e id de la cuenta, incluso si solo aparece una cuenta. Copie el ID correcto en la configuración siguiente. La variable de shell usa 6 bytes aleatorios (12 caracteres hexadecimales) para evitar colisiones con otros estudiantes. Un heredoc escribe el JSON situado entre las líneas JSON; $RUN se expande dentro de él.

La barra invertida delante de $schema conserva esa clave JSON literalmente; $RUN sigue expandiéndose al nombre único de esta ejecución.

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

Sustituya YOUR_ACCOUNT_ID antes de ejecutar el bloque. Mantenga abierto este terminal para que RUN siga disponible. name identifica esta ejecución y account_id selecciona la cuenta para las operaciones en la nube. El archivo es JSON normal, que también es un formato JSONC válido. Escribir este archivo no implementa ningún Worker.

Prepare los tickets y las notas de resolución

En este paso, preparará dos tablas relacionadas. Al cerrar un ticket también debe guardarse su nota de resolución. Si solo se completa una escritura, el personal podría ver un ticket cerrado sin explicación. La configuración proporciona el esquema y el enrutador HTTP; usted implementará las operaciones SQL relacionadas.

Cree una base de datos desechable en la nube. --binding DB proporciona al código de la aplicación un nombre corto, --update-config registra su nombre real y su UUID en wrangler.jsonc, y --use-remote=false mantiene el desarrollo en local:

npx wrangler d1 create "$RUN-db" --binding DB --update-config --use-remote=false

Lea el nombre y el ID creados y, después, inspeccione el binding guardado:

cat wrangler.jsonc

La entrada DB debe indicar la base de datos de esta ejecución. Un binding es una conexión configurada entre el código y un recurso. Su UUID identifica la base de datos en la nube, mientras que --local utiliza una base de datos SQLite independiente en esta VM. Incluya siempre --local o --remote en los comandos SQL.

cat schema.sql
npx wrangler d1 execute DB --local --file schema.sql
npx wrangler d1 execute DB --remote --file schema.sql

El ticket 1 está abierto y no tiene resolución. El ticket 2 está cerrado y es propietario del evento de resolución 1. Ese ID de evento ya ocupado proporciona un caso de error controlado: insertar otro evento 1 debe infringir la clave primaria.

Mantenga las escrituras atómicas y las lecturas secuenciales

En este paso, resolverá dos problemas de coherencia independientes. Atomicidad significa que ambas escrituras relacionadas se completan correctamente o que ninguna de ellas se conserva. batch() de D1 ejecuta las sentencias preparadas como una transacción: si se produce un error, revierte todo el lote. Dos escrituras esperadas por separado no ofrecen esa garantía.

Una sesión registra el estado de la base de datos que ha observado una secuencia de consultas. El enrutador proporcionado llama a env.DB.withSession(...), comenzando en first-primary cuando el cliente no tiene ningún marcador. Envía el resultado de getBookmark() en la cabecera x-d1-bookmark. Una solicitud posterior puede enviar ese marcador para continuar, como mínimo, desde ese estado de la base de datos. Esto es coherencia secuencial, no una transacción de todo o nada entre solicitudes HTTP.

Implemente ambas funciones utilizando la sesión que proporciona el enrutador:

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

Lea src/index.js para localizar withSession, la cabecera del marcador entrante y el marcador devuelto. Todas las operaciones de base de datos de la solicitud utilizan esa sesión. Un marcador es una posición opaca: devuélvalo sin modificar, en lugar de intentar analizarlo o inventar uno.

cat src/index.js
npx wrangler dev --ip 0.0.0.0 > dev.log 2>&1 &
cat dev.log

Espere el mensaje que indica que el servicio local está escuchando. La simulación local puede probar la reversión de un lote, pero no demuestra la replicación remota real ni un marcador de nube.

Observe la reversión antes de cerrar correctamente un ticket

En este paso, enviará deliberadamente el ID de evento que ya está ocupado. La primera sentencia del lote intenta cerrar el ticket 1, pero la segunda falla. Lea el resultado después del error:

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

Debe obtener 409 event_conflict y, después, el ticket 1 todavía open, con un arreglo resolutions vacío. Un 409 por sí solo no es suficiente: la lectura posterior demuestra que no quedó ninguna actualización parcial.

Ahora utilice el ID de evento 2, que está libre:

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

Debe obtener HTTP 200, el ticket 1 con estado closed y el evento de resolución 2 con la nota Access restored. Ambos registros ahora coinciden. No restablezca los datos locales ni modifique la base de datos remota para fabricar un retraso de replicación.

Continúe una sesión remota con su marcador

En este paso, ejecutará el mismo lote contra D1 y llevará un marcador real entre solicitudes. La configuración remota todavía se encuentra en su estado inicial.

npx wrangler deploy

Copie la URL real de la implementación. Primero repita el lote fallido y confirme la reversión:

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"

Debe obtener 409 y, después, un ticket abierto sin resoluciones. Si la implementación todavía se está propagando, vuelva a intentar la lectura durante un máximo de un minuto; no confunda una página de error de la plataforma con el contrato JSON de la aplicación.

Envíe el cierre correcto:

curl -i "$URL/tickets/1/close" -H 'Content-Type: application/json' -d '{"event_id":2,"note":"Access restored"}'

Debe obtener el ticket cerrado y su nota. Copie la cabecera de respuesta x-d1-bookmark no vacía, sin espacios adicionales, en la variable siguiente:

BOOKMARK='YOUR_RESPONSE_BOOKMARK'
curl -i "$URL/tickets/1" -H "x-d1-bookmark: $BOOKMARK"

La solicitud posterior debe observar el ticket cerrado y el evento de resolución 2. Un marcador limita lo antigua que puede ser una lectura; no es un token de autenticación. Este flujo funciona sin exigir una lectura obsoleta ni activar la replicación de lectura. Estamos verificando el contrato de la sesión, no afirmando que se haya producido una condición de carrera entre réplicas.

Abra en el Dashboard el Worker exacto y confirme que su binding DB apunta a la base de datos de esta ejecución. Termine la verificación funcional antes de comenzar la limpieza.

Vinculación del Worker con D1

Este ejemplo muestra la vinculación DB del Worker con su base de datos D1. El prefijo generado identifica esta ejecución de ejemplo; tus nombres serán distintos. La captura solo confirma la vinculación. Las comprobaciones HTTP anteriores verifican la reversión atómica, la actualización correcta y la continuación mediante el marcador.

Elimine los recursos desechables

En este paso, eliminará únicamente los recursos de este laboratorio mientras la VM todavía está autorizada. Termine primero todas las comprobaciones funcionales. Conserve la configuración hasta completar la verificación de la eliminación.

npx wrangler delete

Confirme que solo aparece el nombre del Worker de la configuración de esta ejecución.

npx wrangler d1 delete DB

Inspeccione el aviso y confirme que solo se trata de la base de datos de esta ejecución. Después, enumere las bases de datos:

npx wrangler d1 list --json

El nombre y el UUID de la base de datos que registró deben estar ausentes en una respuesta correcta. Es posible que otros recursos permanezcan. Un error de autenticación o de red no permite sacar conclusiones: resuelva el acceso y repita la lectura antes de continuar. Ejecute la verificación de este paso mientras siga conectado.

Detenga también el trabajo de desarrollo local. Enumere los trabajos y termine únicamente el trabajo wrangler dev que inició; sustituya %1 si su número de trabajo es diferente:

jobs
kill %1

Finalice la autorización de esta VM

En este paso, finalizará la autorización solo después de que la comprobación independiente de eliminación se complete correctamente. Cerrar una VM no equivale a limpiar los recursos en la nube; cerrar sesión elimina la autorización de Wrangler almacenada en esta VM.

npx wrangler logout
npx wrangler whoami --json

Debe aparecer loggedIn: false. Esta consulta sin autenticación puede terminar con un código distinto de cero; eso solo es correcto cuando la respuesta estructurada indica explícitamente que ha cerrado sesión. Complete la verificación y, después, cierre el entorno del laboratorio.

Resumen

Practicó cómo mantener coherentes las actualizaciones relacionadas de un ticket. Comprobó resultados observables de la base de datos, mantuvo explícitos la cuenta seleccionada y el estado local, y eliminó los recursos desechables antes de cerrar sesión.