El problema fundamental que aborda este artículo es la complejidad inherente y las inconsistencias operacionales que surgen de la consistencia eventual en sistemas de bases de datos distribuidas con réplicas de lectura. Aunque la consistencia eventual permite escalar lecturas de manera horizontal, impone una carga significativa en los desarrolladores de aplicaciones, quienes deben implementar lógica compleja y propensa a errores para manejar estados inconsistentes o 'tiempo que retrocede'. Esto se manifiesta en patrones de código como reintentos con sleeps o routing explícito de lecturas al primario, lo que degrada la eficiencia de las réplicas.

La necesidad de consistencia fuerte en lecturas escalables se ha vuelto más apremiante a medida que los sistemas distribuidos se vuelven la norma y las expectativas de los usuarios sobre la coherencia de los datos aumentan. La inconsistencia eventual no solo afecta la experiencia del usuario final, sino que también complica la construcción de microservicios y flujos de trabajo internos, donde las operaciones de lectura-modificación-escritura son comunes y críticas. La tesis central es que, si bien la consistencia eventual tiene su lugar en ciertos escenarios de latencia y disponibilidad, no es adecuada para la mayoría de las APIs y servicios transaccionales, y que es posible lograr consistencia fuerte en lecturas escalables con un diseño de sistema adecuado.

Arquitectura del Sistema

Aurora DSQL logra consistencia fuerte en todas las lecturas, incluso con réplicas escalables, mediante un diseño que desacopla el almacenamiento del cómputo y utiliza un modelo de 'journals' para la propagación de escrituras. Cada réplica de almacenamiento recibe actualizaciones de uno o más journals. Las escrituras en cada journal son estrictamente monótonas, lo que significa que una vez que un nodo de almacenamiento ha procesado una actualización para un timestamp \(\tau\), sabe que ha visto todas las actualizaciones para tiempos \(t \leq \tau\).

Cuando un procesador de consultas inicia una transacción, selecciona un timestamp \(\tau_{start}\). Cada vez que realiza una lectura desde una réplica, solicita datos 'as of \(\tau_{start}\)'. La réplica de almacenamiento verifica si ha procesado todas las actualizaciones de todos sus journals hasta ese \(\tau_{start}\). Si la réplica ha visto timestamps más altos de todos los journals, puede servir la lectura inmediatamente. Si aún no ha alcanzado ese \(\tau_{start}\), bloquea la lectura hasta que los streams de escritura se pongan al día. Este mecanismo garantiza que cualquier lectura, independientemente de la réplica a la que se dirija, siempre vea un estado de datos consistente y actualizado hasta el \(\tau_{start}\) de la transacción, eliminando los problemas de consistencia eventual como 'lecturas no monotónicas' o 'lecturas de tus propias escrituras' fallidas.

Flujo de Lectura Consistente en Aurora DSQL

  1. 1 Cliente Inicia una transacción de lectura.
  2. 2 Query Processor Selecciona un timestamp \(\tau_{start}\) para la transacción.
  3. 3 Query Processor Envía la solicitud de lectura a una réplica de almacenamiento, incluyendo \(\...
  4. 4 Storage Replica Verifica si ha recibido todas las actualizaciones de sus journals hasta \(\ta...
  5. 5 Storage Replica Si está al día, sirve los datos correspondientes a \(\tau_{start}\).
  6. 6 Storage Replica Si no está al día, bloquea la lectura hasta que los streams de escritura se s...
  7. 7 Query Processor Recibe los datos consistentes.
  8. 8 Cliente Procesa la respuesta.
CapaTecnologíaJustificación
storage Aurora DSQL (Custom Storage Engine) Provee un motor de almacenamiento distribuido que desacopla el cómputo del almacenamiento, permitiendo la escalabilidad independiente y la implementación de consistencia fuerte en lecturas. vs MySQL tradicional con replicación statement-based, Bases de datos NoSQL con consistencia eventual
data-processing Journals (Internal) Mecanismo interno para la propagación de escrituras, asegurando un orden monótono y sirviendo como fuente de verdad para la sincronización de réplicas.
compute Query Processor (Internal) Componente que maneja las transacciones, selecciona los timestamps de inicio y coordina las lecturas con las réplicas de almacenamiento.

Fundamentos Teóricos

El problema de la consistencia en sistemas distribuidos ha sido un tema central en la investigación académica durante décadas. La dicotomía entre consistencia y disponibilidad, popularizada por el teorema CAP (Brewer, 2000), aunque a menudo malinterpretada, subraya la dificultad de lograr ambas propiedades simultáneamente en presencia de particiones de red. Sin embargo, el artículo señala que el 'choose-two framing' del CAP es simplista, lo que sugiere una comprensión más matizada de los trade-offs, como los propuestos por el teorema PACELC (Abadi, 2012), que añade latencia como una consideración clave.

La problemática de las 'lecturas no monotónicas' y la necesidad de 'Read Your Writes' (RYW) o 'Monotonic Reads' son conceptos bien establecidos en la literatura de consistencia de datos distribuidos. Doug Terry et al. (1994) en su trabajo 'Replicated Data Consistency Explained Through Baseball' proporciona una introducción accesible a estos términos y a los diferentes modelos de consistencia. El enfoque de Aurora DSQL, al garantizar que las lecturas siempre vean un estado de datos consistente hasta un timestamp determinado, se alinea con la provisión de garantías de consistencia más fuertes que las ofrecidas por la replicación asíncrona tradicional, acercándose a modelos como la consistencia serializable o snapshot isolation, pero aplicado específicamente a la capa de lectura escalada.