Transacciones y workers que compiten
Protege los invariantes cuando varios procesos observan y modifican los mismos datos.
AL TERMINAR, PODRÁS
- Explicar atomicidad y aislamiento por separado
- Identificar una condición de carrera de tipo «comprobar y luego actuar»
- Diseñar una operación para reservar trabajo (claim) con una condición que haga cumplir la base de datos
Descripción del diagrama: 1. Worker A → Base de datos: leer trabajo 7: queued; 2. Worker B → Base de datos: leer trabajo 7: queued; 3. Worker A → Base de datos: reservar 7; 4. Worker B → Base de datos: reservar 7 (¡duplicado!); 5. Base de datos: UPDATE … WHERE status='queued'; 6. Base de datos → Worker B: 0 filas: ganó otro
Un grupo de cambios necesita una frontera
Una transacción agrupa operaciones de base de datos para que se confirmen (commit) juntas o se deshagan (rollback) juntas.
La atomicidad evita que un grupo quede confirmado a medias, pero no hace correcto automáticamente cualquier algoritmo concurrente. El aislamiento (isolation) controla qué pueden observar las transacciones concurrentes y cómo interactúan sus efectos. La durabilidad se refiere a que los datos confirmados sobrevivan a las fallas que cubren la configuración y las garantías de almacenamiento de la base de datos.
Primero enuncia el invariante que necesitas: por ejemplo, que como máximo se emita una reserva vigente para un trabajo pendiente. Luego elige los mecanismos que lo hagan cumplir.
Dos lectores pueden tomar la misma decisión
Imagina que dos workers leen que el trabajo 42 está pendiente.
Luego cada uno lo actualiza a en ejecución. Una transacción alrededor de cada par de instrucciones puede seguir permitiendo este resultado con un nivel de aislamiento común, a menos que la consulta o la estrategia de bloqueo lo eviten. Una opción es usar una actualización condicional atómica y comprobar cuántas filas cambiaron.
Otra es usar bloqueos de fila al seleccionar el trabajo; SKIP LOCKED de PostgreSQL permite patrones de reserva parecidos a una cola. El comportamiento exacto depende de la base de datos, así que no supongas que SQLite y PostgreSQL bloquean igual.
Planifica los conflictos y los efectos externos
Las transacciones serializables (serializable) ofrecen un modelo fuerte, pero pueden abortar transacciones que luego deben reintentarse desde el principio.
Limita los reintentos y asegúrate de que el cuerpo de la transacción se pueda volver a ejecutar sin riesgo. Un rollback de la base de datos no puede «desenviar» un correo ni revertir una llamada a una API remota sin relación. En esa frontera, registra la intención de forma duradera dentro de la transacción y ejecuta el efecto externo en un proceso separado con una estrategia de idempotencia.
En Relay, reservar el trabajo, ejecutarlo y avisar que terminó son pasos distintos, con ventanas de caída distintas.
ERROR FRECUENTE
Envolver una secuencia de lectura y luego escritura en una transacción no reemplaza revisar la semántica de aislamiento y bloqueo que necesitas.
INTÉNTALO TÚ
Escribe el entrelazado de dos workers que leen pendiente antes de que cualquiera escriba en ejecución. Propón una actualización condicional y explica cómo detecta el conflicto el worker que pierde.
Decide en el laboratorio.
El checkpoint de dos preguntas, tus notas y tu progreso guardado están en el laboratorio. Gratis y sin cuenta.
Abrir en el laboratorio ↗CONÉCTALO CON RELAY
Especifica la operación de reserva atómica de Relay y pruébala con dos intentos de reserva que compitan. Documenta el comportamiento de la base de datos que elijas.