Diseño de un Sistema de Validación de Entradas con Código QR: Del Modo Sin Conexión a la Idempotencia
Un revisor escanea un código QR, el sistema lo verifica en la base de datos y lo marca como usado. El requisito principal: una entrada, un acceso. Una tarea aparentemente sencilla se complica por fallos de red, colas y múltiples revisores. Exploremos la evolución de las soluciones, desde la validación sin conexión hasta el procesamiento confiable en el servidor.
El modo sin conexión utiliza una firma digital (ECC o RSA). El código QR contiene los datos de la entrada y la firma; el revisor la verifica con una clave pública sin necesidad de conexión a la red. El problema: falta de sincronización entre revisores. Una captura de pantalla de la entrada puede usarse en cada puerta de forma independiente.
Estado en el Servidor y Actualizaciones Atómicas
Un sistema de almacenamiento centralizado resuelve el problema de sincronización. Cada escaneo es una solicitud al servidor con una actualización de estado atómica.
Ejemplo en PostgreSQL:
UPDATE tickets
SET status = 'used', used_at = NOW()
WHERE id = :ticket_id AND status = 'active'
RETURNING *;
Si se devuelve una fila, la entrada se canjea y se permite el acceso. Si no se devuelve ninguna fila, la entrada ya está usada o no existe.
Un problema clásico: perder la respuesta del servidor. Un revisor en una zona con señal débil envía una solicitud, el servidor actualiza la base de datos, pero la respuesta no llega. Se produce un tiempo de espera, se intenta un nuevo escaneo—el servidor lo rechaza, y el cliente no puede entrar a pesar de tener una entrada pagada.
Idempotencia a Nivel de API
La solución son solicitudes idempotentes con una clave única (UUID), generada al abrir una sesión de escaneo. La clave registra el intento, permitiendo reintentos sin duplicación.
Lógica dentro de una transacción (read committed):
begin ;
-- 1. Registrar el intento
INSERT INTO processed_requests (ticket_id, idempotency_key)
VALUES (:ticket_id, :idempotency_key)
ON CONFLICT (ticket_id, idempotency_key)
DO NOTHING RETURNING *;
-- 2. Si rows_affected == 0: rollback, devolver ÉXITO
-- 3. Canjear la entrada
UPDATE tickets
SET status = 'used', used_at = NOW()
WHERE id = :ticket_id AND status = 'active'
RETURNING *;
-- 4. Si rows_affected == 0: rollback, devolver ERROR
commit;
-- 5. Devolver ÉXITO
La transacción garantiza atomicidad: o se actualizan la clave y el estado, o no se hace nada. Un índice único en (ticket_id, idempotency_key) evita condiciones de carrera—la segunda solicitud espera o va a DO NOTHING.
Un reintento dentro de la misma sesión devuelve éxito, incluso si se perdió la primera respuesta.
Bloqueo en la Cabecera de la Cola y Pérdida de Contexto
Un problema real: un revisor bajo presión de una cola cierra la sesión sin respuesta. Un nuevo escaneo genera un idempotency_key nuevo. El servidor ve una entrada usada con una clave diferente—denegación.
El revisor no puede distinguir a un cliente legítimo de un defraudador con una captura de pantalla. El bloqueo en la cabecera de la cola requiere una respuesta final antes del siguiente ticket.
Un compromiso: devolver la marca de tiempo del último uso. El revisor evalúa: "hace 2 minutos—probablemente la misma persona". Riesgo: un grupo con un código QR compartido pasa secuencialmente.
Desafíos clave:
- Tiempos de espera de red sin pérdida de estado
- Sincronización de múltiples revisores
- Factores humanos en colas
- Equilibrar fiabilidad y experiencia de usuario
Conclusiones Clave
- La actualización atómica UPDATE con condición status='active' evita el canje doble
- La idempotencia mediante UUID + índice único garantiza reintentos sin duplicados
- Las transacciones (read committed) aseguran visibilidad secuencial de cambios
- El bloqueo en la cabecera de la cola es inevitable para una fiabilidad del 100% sin biometría
- El compromiso de la marca de tiempo traslada la decisión al operador
Mejoras Futuras
Los códigos QR estáticos están quedando obsoletos. Los tokens dinámicos o el desafío-respuesta con OTP de un solo uso resuelven el problema. El revisor solicita un nonce al servidor, el cliente lo firma—el servidor lo verifica y lo marca atómicamente.
La biometría (reconocimiento facial) o NFC eliminan las capturas de pantalla pero requieren hardware. QUIC minimiza la pérdida de paquetes pero no resuelve la pérdida de contexto.
Escalabilidad: Redis para almacenar en caché idempotency_keys, fragmentación por ticket_id. Monitoreo: métricas para escaneos fallidos por marca de tiempo, pruebas A/B de compromisos.
— Editorial Team
Aún no hay comentarios.