9 de noviembre de 2012

BLOQUEOS VIRTUALES EN MySQL

De todos es bien sabido la imposibilidad de bloquear registros individuales en MySQL u otras bases de datos de arquitectura Cliente-Servidor. Pero en MySQL, con un poco de imaginación, es posible realizar un bloqueo "virtual"simulando un bloqueo de registro...

BLOQUEOS "VIRTUALES" CON MySQL

De todos es bien sabido la imposibilidad de bloquear registros individuales en MySQL u otras bases de datos de arquitectura Cliente-Servidor. No existe la función RLOCK() como la conocemos para tablas DBF, que permite bloquear un registro o varios en el area de trabajo que le designamos, o que estamos posicionados en ese momento.

Pero en MySQL, con un poco de imaginación, Si es posible realizar un bloqueo "virtual", simulando un bloqueo de registro. Ahora vamos a ver como.

Para ello se utilizan tres funciones propias de la base de datos MySQL:

GET_LOCK(str,timeout)

Intenta obtener un bloqueo con el nombre dado por la cadena str, con un tiempo límite de timeout segundos.  Devuelve 1 si se ha obtenido el bloqueo, 0 si no se ha obtenido en el tiempo indicado (por ejemplo, porque otro cliente ha bloqueado ya el nombre), o NULL si ha habido un error (como falta de memoria o si el hilo fue matado por mysqladmin kill). Un bloqueo se libera cuando se ejecuta la función , se ejecuta un nuevo GET_LOCK() o el hilo termina (ya sea normal o anormalmente). Esta función se puede usar para implementar bloqueos de aplicación o para simular bloqueos de registro. Los nombres se bloquean en bases del servidor. Si un nombre ha sido bloqueado por un cliente, GET_LOCK() bloquea cualquier petición de otro cliente para bloquear el mismo nombre. Esto permite que clientes que se ponen de acuerdo para bloquear un nombre dado usar el mismo nombre para realizar un bloqueo coordinado.

IS_FREE_LOCK(str)

Verifica si el nombre de bloqueo str está libre para usarse (es decir, no está bloqueado). Devuelve 1 si el bloqueo está libre (nadie está usando el bloqueo), 0 si el bloqueo está en uso y NULL si hay errores (como argumentos incorrectos).

RELEASE_LOCK(str)

Libera el bloqueo con el nombre str que se obtuvo mediante. Devuelve 1 si el bloqueo fue liberado, 0 si no fue bloqueado por este hilo (en cuyo caso no fue liberado), y NULL si el nombre de bloqueo no existe. (El bloqueo no existirá si nunca fue obtenido por una llamada a o si ya ha sido liberado.) Es conveniente usar la sentencia con RELEASE_LOCK().

Vamos a ver un ejemplo de su utilización con VFP y a través de SQL Pass Through.
El ejemplo va a consistir en el bloqueo de un código determinado, es indiferente que sea un código de Cliente, de Proveedor, de un Artículo, o un número de Factura. Para el ejemplo vamos a seguir tres pasos:
  • Conectarnos a la base de datos MySQL
  • Realizar un bloqueo virtual a un código determinado (LOCK)
  • Liberar dicho bloqueo (UNLOCK)
Conexión a una base de datos MySQL.
cSQL=  "DRIVER={MySQL ODBC 5.1 Driver};" + ;
       "SERVER=localhost" + ;
       "PORT=3306" + ;
       "UID=xxxx" + ;
       "PWD=xxxx" + ;
       "DATABASE=MyBaseDatos" + ;
       "OPTION=2049"
 
SQLSETPROP(00,"Transactions", 1) 
SQLSETPROP(00,"DispLogin", 3)          
 
NH = SQLSTRINGCONNECT(""+cSQL, .T.)
IF NH < 0
MESSAGEBOX("Error de CONEXIÓN : " + ALLT(STR(NH)),48,"Atencion")
ENDIF

Realizar un bloqueo virtual 

El ejemplo muestra el intento de bloqueo de un código almacenado en la variable cKey . La función GET_LOCK reintenta automáticamente el bloqueo durante 10 segundos. Si consigue el bloqueo exitosamente devuelve el valor ‘1’, y salimos del bucle DO WHILE. Si al cabo de 10 segundos no es así, el valor devuelto será ‘0’ o Nulo con lo cual mostraremos un mensaje indicando que el código ya se encuentra bloqueado, o que su bloqueo es imposible.

También se puede utilizar la función IS_FREE_LOCK, para comprobar si el código en cuestión se encuentra bloqueado.
cKey="000001" && Código a Bloquear

DO WHILE .T.

     cSQL="SELECT IFNULL(GET_LOCK('"+cKey+"',10),'0') AS cRESULT"   
     SQLEXEC(NH,""+cSQL,"CURSOR")
     SELECT CURSOR
     GO TOP
     IF ALLTRIM(CURVAL(""+FIELD(1),"CURSOR"))="1"
          EXIT     && Código Bloqueado
     ENDIF

     OPB=MESSAGEBOX("Error de BLOQUEO   Reintentar",5+48+0,"Atencion")
     IF OPB <> 04
          EXIT
     ENDIF

ENDDO

Liberar Bloqueo


Liberar los bloqueos es lo más sencillo con la función DO RELEASE_LOCK. Si se consigue liberar el código del bloqueo realizado por el mismo usuario el valor devuelto por la función es "1". No es necesario tener un control muy exahustivo sobre el resultado de la función, ya que el hilo que la ejecuta debe ser el mismo que ha realizado el bloqueo anteriormente.
cSQL="DO RELEASE_LOCK(''+cKey)"
SQLEXEC(NH,""+cSQL)

Detectar si un Código se encuentra bloqueado

otra función que también se utiliza es IS_FREE_LOCK, nos servirá para detectar si el código que vamos ha bloquear se encuentra libre de bloqueos. Si es así nos devolverá el valor "1". Se puede utilizar conjuntamente con la función GET_LOCK, dentro de un bucle de comprobación y realización de bloqueos.
cKey="000001" && Código a Bloquear

DO WHILE .T.

     cSQL="SELECT IFNULL(IS_FREE_LOCK('"+cKey+"'),'0') AS cRESULT"   
     SQLEXEC(NH,""+cSQL,"CURSOR")
     SELECT CURSOR
     GO TOP
     IF ALLTRIM(CURVAL(""+FIELD(1),"CURSOR"))="1"

            ** Código libre para Bloqueo (Bloqueamos)

            cSQL="SELECT IFNULL(GET_LOCK('"+cKey+"',10),'0') AS cRESULT"
            SQLEXEC(NH,""+cSQL,"CURSOR2")
            SELECT CURSOR2
            GO TOP
            IF ALLTRIM(CURVAL(""+FIELD(1),"CURSOR2"))="1"
                 EXIT     && Código Bloqueado Salir 
            ENDIF

    ENDIF

    OPB=MESSAGEBOX("Error de BLOQUEO   Reintentar",5+48+0,"Atencion")
    IF OPB <> 04
          EXIT
    ENDIF

ENDDO

Gráficamente este sería el resultado real de todo lo que hemos explicado hasta ahora, y su utilización práctica. En la imagen inferior se muestra el mensaje de bloqueo que aparece cuando un usuario intenta acceder en este caso, a modificar una factura utilizada en este preciso instante por otro usuario.



Antonio L. Montagut
(www.ontarioxb.es)

No hay comentarios. :

Publicar un comentario