Imaginemos que tenemos una tabla de un esquema de Oracle (o base de datos en cualquier otro SGBD) a la que dos instancias P1 y P2 de una aplicación acceden simultáneamente, pero queremos que las filas que se procesen en P1, no se procesen en P2, y viceversa. Un mecanismo válido para evitar la repetición es mediante el bloqueo de filas, ya sea a través de una tabla auxiliar o de una columna especial en la tabla original. Nos concentraremos en el primer caso.
Tenemos la tabla A con llave primaria A1 y tabla LOCK_A con llave primaria LOCK_A1 del mismo tipo que A1. Además, LOCK_A contará con una columna de tipo fecha/hora (DATE o TIMESTAMP, por ejemplo) llamada LAST_DATE. Se bloqueará una cantidad N de filas, pues los datos serán procesados por lotes. La tabla R contendrá las filas de A que se procesarán.
- Obtenemos un cursor resultado de la consulta:
SELECT A1 FROM A WHERE A1 NOT IN (SELECT LOCK_A1 FROM LOCK_A) - Comenzamos un ciclo que recorre cada fila del cursor.
- Comenzamos una transacción. BEGIN
- Con la fila actual, insertamos A1 en la columna LOCK_A1 en LOCK_A, más la fecha y hora actual (SYSDATE).
- Confirmamos la transacción. COMMIT
- Si no se produjo un error (llave primaria violada), guardamos la fila en R.
- Terminamos la transacción.
- Devolver R.
Pero también necesitamos que si ha pasado un tiempo dado, las filas se desbloqueen, por si falló alguna de las aplicaciones y otra las pueda procesar. Para ello, antes de bloquearlas, ejecutamos un comando similar al siguiente:
DELETE FROM LOCK_A WHERE SYSDATE - LAST_DATE > TIMEOUT
En este caso, TIMEOUT es un parámetro numérico (ver aritmética de fechas en Oracle).
Dos cosas a tener en cuenta:
- LOCK_A debe tener activada la restricción (constraint) de llave primaria sobre la columna LOCK_A1, para que en caso de insertar la fila con una llave existente se produzca un error.
- Notar en el paso 5, COMMIT deberá hacerse justo después de INSERT, pues si se hace al final del ciclo, puede que alguna de las inserciones provoque un error que impida que las demás se puedan realizar.
Visto así, todo debería funcionar correctamente. Pues no. Puede suceder que las aplicaciones procesen las mismas filas.
¿Cómo? Si la instancia de Oracle está configurada en clúster, tenemos más de una computadora que gestiona los comandos sobre la base de datos. Si, por casualidad, un nodo del clúster tiene una fecha u hora distinta a los otros nodos, o está en una zona horaria diferente, SYSDATE dará fechas diferentes según el nodo que procese la solicitud de Oracle. Por lo que una operación como SYSDATE - LAST_DATE u otra semejante puede dar números alterados, hasta valores negativos, algo que en el sentido común puede parecer imposible. Este ligero cambio, bastante difícil de detectar, puede provocar que las filas se desbloqueen en menos o más tiempo del esperado.
Solución:
- Mantenga siempre las computadoras de un ambiente distribuido con la configuración de hora, fecha y zona horaria sincronizada.
- Cree una herramienta para determinar automáticamente que todas las condiciones estén creadas para que su sistema funcione con eficacia.
No hay comentarios:
Publicar un comentario