En un artículo anterior se sustituyó la columna status de la tabla users por una tabla append-only user_statuses, lo que permite conservar el historial completo de cambios de estado de cada usuario. Sin embargo, este diseño plantea un problema de concurrencia que la columna resolvía implícitamente: si dos transacciones leen el estado actual antes de que ninguna confirme, ambas pueden insertar su propia fila y dejar registrada una transición que nunca debió producirse. Un ejemplo típico es el de dos administradores que, simultáneamente, aprueban y rechazan al mismo usuario partiendo del estado pendiente.
Con la columna tradicional, Postgres serializa los UPDATE y el último escritor gana; con la tabla append-only no existe esa sobrescritura natural, de modo que ambas filas conviven. Para evitarlo, el artículo propone bloquear la fila del usuario padre con SELECT ... FOR UPDATE dentro de la transacción. Así, la segunda transacción queda bloqueada hasta que la primera confirma y, al desbloquearse, lee el estado ya actualizado para tomar una decisión informada. Funciona bajo el nivel de aislamiento READ COMMITTED, el predeterminado, sin cambios de configuración.
El bloqueo serializa el acceso pero no rechaza por sí solo las transiciones inválidas, por lo que se recomienda complementarlo con una comprobación explícita basada en un mapa de transiciones permitidas (por ejemplo, null → pending, pending → approved o denied). Si la transición no es válida, la segunda transacción hace ROLLBACK. El artículo también compara esta estrategia con el nivel SERIALIZABLE de Postgres, que introduce falsos positivos por la elevación de bloqueos de predicado, lógica de reintentos y sobrecarga operativa, por lo que SELECT FOR UPDATE resulta más simple y predecible para este caso.
