miércoles, julio 27, 2005

Bloqueos de Datos en RED

Este Documento tiene algun tiempo, pero he decidido publicarlo ya que aun hay muchos que se estan iniciando en xBASE ( Harbour o xHarbour ) y estan aprendiendo a realizar sus primeras aplicaciones en red, espero les sirva.
Consideraciones para el bloqueo de Archivos de bases de datos en RED.

El uso de plataformas de red y Bases de Datos Relacionales (RDBMS) para las aplicaciones de administración y manejo de datos es cada vez mas común y esta al alcance de todo tipo de organizaciones, aun de negocios catalogados de pequeños, los recursos necesarios para implementar una red son cada vez menores y la tecnología para su levantamiento es fácil de encontrar y conocer.

Con esto en mente los desarrolladores de aplicaciones se ven enfrentados a la necesidad de poder diseñar bloqueos al acceso de los datos de acuerdo a la realidad que presenta el múltiple acceso que permite el Software de Red o la Base de Datos. Por otro lado tenemos los accesos por WEB que cada vez se hacen mas comunes y donde podemos consultar nuestros datos desde cualquier parte del mundo que tenga un acceso a Internet.

Si programamos desde nuestro querido Clipper o actualmente xHarbour hasta Administradores de bases de datos tan poderosos como Oracle, MS-Sql, MySql y otros, debemos diseñar nuestro sistema para que permita un manejo transparente al usuario y que sea capaz de representar la realidad del flujo de información que queremos controlar.

EL problema comienza cuando pensamos que nuestra aplicación estará siendo utilizada por al menos dos estaciones al mismo tiempo y que cada una de ellas accederá a nuestras bolsas de datos en forma concurrente. Es utópico pensar que en ningún momento se producirán cruces de solicitudes de datos y en mas de alguna oportunidad los datos no podrán estar disponibles para una estación al estar ocupados por otra.

Para entender el enfoque a utilizar al momento de diseñar el flujo de información que administrara nuestra aplicación debemos recordar las características de uso, bloqueo y permisos necesarios para acceder a bases de datos.

Si bien es cierto que utilizaremos para ello los archivos .dbf ( específicamente aquellos administrados por los lenguajes xBase ) podemos traspasar sus características y requerimientos sin problema alguno a otro administrador de bases de datos como MySql, ya que la tecnología de uso y bloqueo de archivos, tablas, índices u ordenes es común para ambos salvo las instrucciones y comandos propios de cada uno. La solución de diseño implementada para uno es la misma que utilizaremos en la otra, solo cambiara la forma de programarla y controlarla.

¿Porqué son importantes los bloqueos?

Un bloqueo permite, por ejemplo, que un usuario de una maquina X pueda acceder en forma directa a un dato y modificarlo sin que otros usuarios en maquinas distintas puedan utilizar dicho dato hasta que este halla sido finalmente liberado y escrito en el medio de almacenamiento del cual proviene. Con esto aseguramos la integridad y pertinencia de los datos almacenados.

Si estamos en el proceso “cambio_de_precios” , tenemos la posibilidad de bloquear los registros ( filas ) de productos para que mientras escribimos el nuevo monto ningún usuario pueda utilizarlo y así asegurarnos que los datos se grabarán con éxito.

Ciertos usuarios de la aplicación tendrán más derechos que otros al momento de acceder a nuestra fuente de información, no podemos negar la opción al Gerente de la empresa de modificar un precio a su antojo porque un vendedor esta visualizando un dato del producto ( un usuario de menor jerarquía ). De la misma forma el contador de la empresa no podrá en cierto momento acceder a los datos de stock de un determinado producto porque en ese mismo instante se esta generando una venta ( un proceso de mayor jerarquía ).

Un factor importante al momento de pensar en un bloqueo ( sea de bases de datos, tabla, registro ) es el tiempo en que este estará inaccesible para el resto de los usuarios, el bloqueo deberá ser lo mas rápido posible y deberá entregarnos las opciones necesarias en caso de fallar o de estar momentáneamente ocupado.

Tipos de Bloqueos

Uso EXCLUSIVO de archivo : El uso exclusivo de un archivo permite que solo un usuario pueda tener acceso total a él. Cuando nos referimos a acceso total, nos referimos a la posibilidad de leer, modificar, eliminar y guardar datos al interior del archivo. Es el método mas restrictivo que existe ya que solo permite a un único usuario acceder a un archivo y evita que otros puedan inclusive leer o visualizar su contenido. Este tipo de uso exclusivo era el predeterminado en los viejos tiempos de DOS, aun alejados de los NOS ( Network Operating Systems o Sistemas Operativos de Red y de la WEB).

Su ventaja principal ( si la consideramos ventaja ) es la simplicidad al momento del diseño y programación ya que nos entrega poder absoluto sobre el archivo y no debemos preocuparnos de un posible doble acceso al archivo o su contenido.

Su desventaja principal radica en que si utilizamos este enfoque en un ambiente de red, el primer usuario que tome control del archivo se convierte en su dueño absoluto hasta que lo grabe y deje de utilizarlo, permitiendo en este momento que otro usuario pueda accederlo y nuevamente se transforme en su único dueño.

Para acceder a un archivo .dbf en modo exclusivo desde xBase lo haríamos de la siguiente forma, agregando la cláusula EXCLUSIVE al comando USE

Select 1 //Seleccionamos el área de Trabajo
Use Clientes EXCLUSIVE // Agregamos la Cláusula de uso Exclusivo

Si no queremos especificar el modo de apertura del archivo cada vez que deseemos utilizar un .dbf podemos utilizar en comando SET EXCLUSIVE al inicio de nuestra aplicación y así asegurarnos que TODOS los archivos se abrirán en modo exclusivo

SET EXCLUSIVE ON

Para cambiar el modo de apertura desde algún punto de la aplicación en adelante solo utilizamos su variante…

SET EXCLUSIVE OFF

Su uso no es aconsejable al momento de programar nuestras aplicaciones ya que es demasiado restrictivo.

Es aconsejable abrir los archivos en modo compartido e intentar un bloqueo al archivo si es que realmente necesitamos control sobre el archivo. En ocasiones de un borrado de mas de un registro ( fila ), reordenamiento, reemplazo de mas de un registro, copia desde otra fuente de datos y algunas que Uds. estimen pertinentes.

Bloqueo de Archivo (Tabla) : La contraparte del uso exclusivo de un archivo es el uso compartido, este permite que mas de un usuario o aplicación puedan acceder a una fuente de datos, sin embargo el usuario que obtenga el bloqueo de archivo podrá hacer modificaciones sobre el mientras que los demás solo podrán acceder a visualizar su contenido. Aún así solo un usuario tiene control absoluto sobre el archivo.

Como habíamos mencionado, si se requiere modificar mas de un registro desde una .dbf o tabla esta opción es la mas permisiva al facilitar a los demás usuarios a lo menos la posibilidad de visualizar el contenido del archivo.

Personalmente no lo recomiendo pues creo que si se necesita modificar más de algún registro no es necesario un bloqueo de archivo, es preferible escribir una rutina que se llame las veces que sea necesario y utilice bloqueos individuales antes que bloquear el archivo completo.

Para los procesos de mantenimiento de las estructuras de datos , sean bases de datos, tablas, archivos .dbf, índices u ordenes es preferible hacerlo “Off Duty”, fuera de los periodos de utilización de los archivos y así asegurarse de tener control absoluto para esas tareas.

Bloqueo de Registro (fila) : Este nivel de bloqueo de datos es el mas flexible y el que mas utilizaremos.

Un bloqueo de registro es una solicitud a la red o al RDBMS de bloquear una porción del archivo en uso. Si esta porción del archivo esta disponible nuestro bloqueo es exitoso y el Sistema Operativo de Red actualiza su tabla de bloqueos para reflejar que esa porción de archivo esta siendo utilizada, de esa manera si alguien mas intenta bloquear ese registro el “S.O.R.” devuelve FALSO y no realiza el bloqueo.

Aquí necesitamos entender que si el bloqueo se mantiene en el tiempo, el registro no estará accesible para ningún usuario hasta que el “dueño” lo libere. Es muy importante desarrollar una estrategia para que el bloqueo sea lo mas corto y efectivo posible. Eso lo veremos con detalle mas adelante.

El bloqueo de registro devuelve verdadero o falso dependiendo de si esta disponible o no para ser bloqueado, sin embargo no nos entrega mayor información de quien, que proceso o que maquina esta haciendo uso de el. Una alternativa a esas preguntas es el uso de Bloqueos por Semáforos.

Boqueos por semáforos : Este tipo de bloqueo es muy eficiente pero es mas difícil de implementar.

Si nuestro Sistema Operativo de Red o RDBMS permite el uso de Semáforos tenemos mas de la mitad del trabajo realizado, dependerá de las características de semáforos que utilice y su implementación dependerá de la cantidad de documentación con la que contemos. Personalmente he indagado un poco en el uso de los disponibles en Netware y he logrado buenos resultados.

El bloqueo por semáforos es un signo, señal entre dos procesos, usuarios que les indica como y si pueden o no operar. Un semáforo de red es una etiqueta o un numero indeterminado de caracteres utilizados para comunicar información. Junto con la etiqueta, el semáforo posee un valor numérico. Las aplicaciones deben coincidir en el uso de los semáforos para lograr consistencia. Si el semáforo no existe se crea, si el semáforo esta siendo utilizado se le incrementa un digito en el valor asociado, de esa forma se conoce cuando un dispositivo, tabla, registro o dato esta siendo utilizado o no.

Su implementación es bastante mas compleja y existen condiciones inherentes a cada Sistema Operativo de Red que no podemos discutir en extenso en este pequeño documento pero si le invitamos a investigar.

Sistema de Bloqueos de Lenguajes Xbase

Utilizaremos al Compilador Clipper como modelo para explicar un diseño de bloqueos eficiente en nuestras aplicaciones. Su estructura es identica a la utilizada por Harbour y xHarbour. Primero entenderemos sus caracteristicas y luego iremos al detalle.

Clipper es rápido debido a sus características y opciones de manejo de buffer de datos, en una maquina sin conexión a red veremos que las aplicaciones corren mas rápidas que si estuvieran en un servidor de archivos. Esto es debido a que Clipper no solo carga en memoria los datos del registro actual en uso sino que carga una cierta cantidad de registros ubicados antes y después del actual, un paseo por una .dbf es rápido y puede verse como la lectura de nuestro disco duro es cada cierto tiempo y no en forma continua.

Cuando una .dbf es abierta en modo exclusivo, el comando USE establece un buffer en memoria y lee cuantos registros es posible y los carga es ese espacio asignado. Si por el contrario el archivo .dbf es abierto en modo compartido, utilizado en red, solo el registro actual es incluido en el buffer, he aquí la explicación de porque se reduce la performance en un ambiente de red.

Si por otro lado tenemos un índice abierto llenar un buffer con datos es innecesario ya que si nos movemos al siguiente registro en el orden asignado por el índice puede que físicamente se encuentre al final del archivo .dbf y no necesariamente al lado.

Por otro lado existen los buffer de .ntx/.cdx, cuando un comando SET INDEX TO es usado, este llena un buffer de tamaño variable en un espacio especial de memoria o en memoria expandida, entonces la única manera de asegurarse que los datos van directamente al disco duro es mediante el uso del comando COMMIT, este vacía los buffer, escribe en el disco duro, verifica si el buffer del índice corresponde a la imagen o no del archivo .dbf y lo reescribe.

El uso de COMMIT es aconsejado cada ves que se requiera, no hay problema en usarlo indiscriminadamente ya que un switch interno se utiliza para verificar si el COMMIT es realmente necesario o no.

El proceso que inicia el comando COMMIT se traduce en el vaciado de los buffer desde Clipper a DOS y luego a disco duro, en el caso de una estación no conectada a red es de la siguiente forma :

Buffer Clipper -> Buffer DOS -> ( Cache de Disco Duro ) -> Disco Duro

Si por el contrario esta estación esta utilizando la aplicación en modo multiusuario en una red cualquiera se traduce asi :

Buffer Clipper -> Buffer DOS -> SOR ->Servidor de Archivos -> Disco Duro

Hay que hacer notar que el Sistema Operativo de Red obliga a escribir al Servidor de Archivos pero este puede o no vaciar directamente al disco duro, dependerá de los niveles de cache del servidor, Servidor de Archivos y del tipo del equipo utilizado.

Un consejo :
Si un archivo .dbf es procesado en forma secuencial, de inicio a fin, se recomienda cerrar los índices ya que cada movimiento en el archivo requiere de un bloqueo del índice traduciéndose en una demora innecesaria, para ello solo activar el orden natural del archivo .dbf con SET ORDER TO 0.

Diseño de Bloqueos


Para un buen diseño de los bloqueos al interior de nuestra aplicación debemos considerar dos cosas.
1.- Persistencia al bloquear
2.- Como solucionar bloqueos fallidos

1.- Persistencia al bloquear. Nuestra función de bloqueo ( de archivo o registro ) debe permitirnos insistir en el bloqueo por el tiempo o numero de veces que le asignemos. El enfoque de “intentar bloqueo -- fallo -- desistir del bloqueo” no es aconsejable, lo ideal es entregar al usuario la posibilidad de intentarlo hasta que el lo desee o hasta que cierta cantidad de tiempo se halla cumplido

2.- Como solucionar bloqueos fallidos. Por otro lado debemos implementar la solución al manejo de los registros que se intentaron bloquear y que no fueron obtenidos en forma exclusiva, si se trata de un proceso que intenta modificar varios registros en una tabla, nos quedamos pegados en el primero que esta siendo ocupado por otro usuario, continuamos con el siguiente, que hacemos con el que no pudo actualizarse, cuando y como volvemos a intentar su bloqueo y actualización… Son buenas interrogantes que lo mas seguro tengan una respuesta si lo piensas un poco.

Así como cual será el manejo del bloqueo también es importante la forma en que se realizara la actualización del registro. Para ello existen dos formas de realizarlo.

Bloquear -> Leer Datos -> Modificar Datos -> Escribir datos -> Desbloquear

Aquí encontramos nuestro registro, leemos los datos que contiene y realizamos las modificaciones estipuladas, luego vaciamos los buffer y escribimos en el disco duro y finalmente desbloqueamos el registro para que este disponible.
El problema con este enfoque es que si el usuario logra bloquear y leer el registro, comienza a modificarlo y repentinamente surge un imprevisto, como un accidente, es llamado por el jefe, recibe una llamada telefónica importante o etc.. y deja el registro tomado en pantalla, nadie podrá acceder a el hasta que el vuelva.

La solución es crear una función que administre la cantidad de tiempo que el registro esta bloqueado, si excede los limites establecidos, que automáticamente vacíe los buffer y desbloquee el registro. Aquí es necesario modificar la funciones capturadoras de datos en pantalla ( en Clipper los GET ) para añadir esa opción de control de tiempo.

Personalmente y a pesar de haber escrito dicha función, no es el enfoque que utilizo, prefiero el siguiente…

Leer Datos -> Editar Datos -> Bloquear -> Escribir Datos -> Desbloquear

Aquí lo que realizamos es una lectura del dato, modificamos una “imagen” del registro, cuando la modificación este terminada, solicitamos el bloqueo, escribimos los datos y desbloqueamos. Es el mas rápido y efectivo, el registro solo esta bloqueado lo que dura la escritura a disco ( algunos milisegundos ) y podemos demorarnos lo suficiente durante la edición.

Sin embargo aquí surge otro problema mas, que pasa si otro usuario también requiere una modificación del registro. Imaginemos la situación…

Pedro necesita modificar el precio de venta de un producto y Maria por su lado necesita escribir la descripción técnica del mismo producto.

Pedro fue el primero en iniciar la labor que le corresponde, lee el registro, carga la imagen del registro en pantalla y empieza la modificación, es llamado desde gerencia en forma urgente y deja su trabajo a medio terminar…

Maria por su lado toma otra imagen del registro, la modifica, escribe el campo del detalle técnico, bloquea, graba y desbloquea el registro…

Pedro vuelve de su reunión, retoma la modificación del registro en pantalla, finaliza, bloquea, graba y desbloquea…..¿?

Aquí se ha perdido el trabajo de Maria, ya que los últimos datos que se grabaron fueron los de Pedro, el tenia una imagen del registro sin los datos del detalle técnico, como se demoro en realizar su labor y escribió sus modificaciones después de su compañera, lo que hizo fue borrar el trabajo de ella.

Fácilmente se le puede acusar ( a ella ) de no haber realizado su labor… un descuido en este tipo de situaciones que puede llevar a problemas de tipo laboral, en el extremo de la situación.

Cuando fui requerido del desarrollo de una aplicación de control de Stock y Modulo de Ventas pense largamente en como enfrentar la actualización de los stocks, listas de precio y el primer recurso a mi haber era que no había diseño previo ni preconcepciones de ningún tipo al momento de diseñar y modelar.
La solución practica a mi problema, que era el archivo que contenía los datos de los productos y sus múltiples accesos por diferentes instancias de la empresa, fue dividirlo en cuantas instancias fuera necesario según los distintos procesos que lo requerían.

Así un monstruoso archivo .dbf de mas de 50 campos que diseñe al inicio de mi labor quedo reducido a 5 archivos .dbf con caracteristicas muy especificas cada uno.

Producto.dbf //Codigos, descripción del producto
Stocks.dbf // Stocks de los diferentes locales
Precios.dbf // Lista de Precio de los Productos
Cierres.dbf // Información del movimiento del Producto
Tecnico.dbf // Detalle y descripción técnica de cada producto

Así cada proceso y usuario bloquea el registro, archivo que requiere en un momento y minimice los “choques” de bloqueos en gran medida, el ordenamiento del sistema es mas fácil de llevar, y aunque tengo mas archivos abiertos en ciertos procesos no he tenido ninguna perdida “bruja” de datos.

“Dividir para conquistar” en mi caso esta premisa resulto, sin embargo como expuse al inicio de este documento instructivo, la intención del mismo es entregar algunas consideraciones al momento de diseñar instancias de bloqueo de archivos y datos al planear aplicaciones que corren en red.

Pretender mas que eso es inútil ya que existen libros y libros de diseño de aplicaciones y cada lenguaje en particular tiene mas o menos características en cuanto al manejo de datos, buen capitulo para empezar es ver los bloqueos en el lenguaje que utilizas, las posibilidades que brinda tu Sistema Operativo de Red y una imagen clara del flujo de información al interior de la empresa, la utilización de jerarquías ( tanto de procesos como de usuarios )

Por otro lado la utilizacion de los RDBMS esta tan difundida y tan accesible que es muy dificil no imaginarse a los herederos de Clipper (Harbour y xHarbour) no poder administrarlos, para ellos existen cientos de manuales y documentos tecnicos especificos sobre el uso de los bloqueos en casi todos los RDBMS que yo conozco, Ms-Sql, Oracle, Sybase, PostGress, MySql, Interbase, etc. Cuento aparte lo es ADS y Apollo que son la misma estructura de los DBF nos entregan un sistema Cliente Servidor muy potente.

Espero que te sirva

Adolfo Lagos Jimenez

1 Comments:

At 5:12 a. m., Anonymous Anónimo said...

Felicitaciones por el blog y por el artículo. Uno se encuentra con muchos programadores a los que les falta leer tu artículo más de una vez.
Respecto a la forma de abrir las tablas cerrando los indices cuando la ocasion lo permite, tambien es muy interesante hacer la apertura READONLY, que normalmente es muy ignorada. Mejora la perfomance y disminuye el riesgo de que por un error se alteren los datos donde no se debe. Un USE ... EXCLUSIVE READONLY es *increiblemente* rápido.

Respecto de los bloqueos concurrentes, creo que lo de leer, alterar, bloquear, grabar, desbloquear se llama bloqueo optimista, versus el bloqueo pesimista de bloquear, leer, alterar, grabar, desbloquear.

En nuestro ambiente está considerado siempre el bloqueo físico (bueno, en la realidad no es tan asi pq sino no lo podriamos leer, pero a efectos practicos...). Pero una solucion está dada por el bloqueo doble: logico y físico.
El fisico es el que provee el lenguaje, el logico debemos implementarlo, por ejemplo, agregando un campo a una tabla donde indicamos quien posee el bloqueo logico del registro, indicando el nombre de usuario o alguna referencia similar, y administrar las contingencias que aparezcan. De esa manera, en el caso de la pobre empleada que le plancharon su trabajo, se hubiese enterado de que alguien estaba ya realizando esa tarea.

Un saludo y te reitero las felicitaciones

 

Publicar un comentario

<< Home