sábado, 12 de noviembre de 2011

Los niveles de aislamiento en PostgreSQL (no son 4)



"No. No lo intentes. Hazlo, o no lo hagas, pero no lo intentes."

Cuando ví el Episodio IV de la Guerra de las Galaxias ("El Imperio Contraataca"), aún era un niño impresionable, y me llamó la atención la reprenda que hizo Yoda a Luke y que cito aquí. Era uno de ésos consejos marciales que se enuncian solemnes como un consejo vital.

Con el tiempo te das cuenta que como consejo está bien, y puede ser incluso un buen objetivo... pero en ciertas facetas de la vida no es válido. Por ejemplo, en el mundo científico y en nuestro mundo profesional el consejo es justamente el contrario: inténtalo, pruébalo y compruébalo. Y sólo cuando lo hayas hecho y consigas un resultado repetible, ponlo en práctica, esto es, súbelo a producción.

Gracias a no seguir el consejo de Yoda fui consciente de una (grave) carencia de PostgreSQL con respecto a los niveles de aislamiento. Como ya hice una introducción al aislamiento en otro artículo, no voy a extenderme más en el tema y voy al grano. El caso que me ocupaba es que tenía que hacer una importación transaccional de datos masiva en un sistema en OLTP de producción en el que se están modificando constantemente datos relacionados con los que voy a importar. El resultado de la importación es que la transacción gigante bloquea datos e impide que se efectúen modificaciones en datos relacionados paralizando en la práctica buena parte de las operaciones de producción produciéndose un efecto "bola de nieve" al sumarse cada vez más transacciones en cola a la transacción gigante pendiente. Sin embargo, para lo único que quiero la transacción es para tener atomicidad en la operación, es decir, para que no se quede a medias en caso de algún problema, pero no me importa que se puedan producir eventualmente casos de dirty read, que no afectarían a los usuarios del sistema de producción en su trabajo. Vale, no hay problema. La transacción de inserción la podemos realizar con el nivel de aislamiento más bajo (READ_UNCOMMITED) y problema solucionado. ¿No? No obstante, no hagamos caso a Yoda, y probémoslo en pre-producción. Resultado: exactamente el mismo que si no hubiera especificado nada del nivel de aislamiento.

¿Qué ha pasado?

En la documentación de SET TRANSACTION, en efecto compruebo que PostgreSQL admite los cuatro niveles de aislamiento como la mayoría de las bases de datos (de hecho, la sentencia no dio error)... pero sólo sintácticamente: no están todos implementados. Si contiúas leyendo te das cuenta que no son 4, sino 2 los niveles implementados:
"The SQL standard defines two additional levels, READ UNCOMMITTED and REPEATABLE READ. In PostgreSQL READ UNCOMMITTED is treated as READ COMMITTED, while REPEATABLE READ is treated as SERIALIZABLE."
Lo que viene a decir: "¿Pensabas que eran cuatro, no? Pues no. Son dos". De hecho, ahora (con la versión 9.1) ya son 3. Pero sigue sin estar soportada la lectura sucia (dirty read). En la documentación sobre aislamiento también es bastante claro:
"In PostgreSQL, you can request any of the four standard transaction isolation levels. But internally, there are only two distinct isolation levels, which correspond to the levels Read Committed and Serializable. When you select the level Read Uncommitted you really get Read Committed, and when you select Repeatable Read you really get Serializable, so the actual isolation level might be stricter than what you select."

Un verdadero jarro de agua a mis pretensiones, ya que no puedo conseguir atomicidad sin bloqueo (aunque sea realizando lecturas de transacciones no confirmadas) en PostgreSQL, al menos con la versión actual 9.1.


Referencias y más información:

    Related Posts Plugin for WordPress, Blogger...
    cookieassistant.com