11 de noviembre de 2015

Buffer y bloqueos en Visual FoxPro - Resumen

Artículo original: Buffering and Locking in Visual FoxPro - an Overview
http://weblogs.foxite.com/andykramek/archive/2005/10/18/948.aspx
Autor: Andy Kramek
Traducido por: Ana María Bisbé York


¿Dónde comenzamos?

Parece que trabajar con datos en buffer en Visual FoxPro causa mucha confusión. Creo que es en gran medida debido a la confusión en la implementación del buffer y en parte también, por problemas de nomenclatura (por estándares convencionales) asociados al tema.

Por ejemplo, para definir buffer para un archivo DBF de Visual FoxPro, que es una tabla, tenemos que utilizar la función CURSORSETPROP(), que está excesivamente sobrecargada. ¿Por qué no tener una función separada, sin ambigüedades, " SetBufferMode()"? Para confirmar una transacción pendiente el comando es END TRANSACTION. ¿Por qué no "COMMIT" como en el resto de los lenguajes de bases de datos - una opción que incluso es más peculiar ya que el estándar "ROLLBACK" se utiliza para revertir una transacción?

Además, quizás debido a que Visual FoxPro implementa el bloqueo de los registros (lo contrario a "Página"), el hecho de controlar la colocación y liberación de los bloqueos está inseparablemente enlazada con el buffer. Por ejemplo, para permitir buffer de filas, (que solamente opera sobre un sólo registro) SET MULTILOCKS debe estar en "ON". De acuerdo con el archivo Ayuda, esta configuración solamente determina la capacidad de Visual FoxPro para bloquear múltiples registros en la misma tabla - y que de todas formas, está limitado a la actual sesión de datos. Esto no parece ser muy lógico y no es realmente sorprendente que la gente esté totalmente confusa.

¿Qué significa "buffer"?

El principio es realmente muy simple. Cuando hace cambios en algún dato, esos cambios no es escriben en la tabla fuente, en lugar van a un área de almacenaje (el "Buffer") hasta el momento en que le indica a Visual FoxPro que deben ser guardados en el almacén permanente o eliminarlos. Esta área de almacenaje es lo que, en la actualidad, ve en Visual FoxPro cuando se utiliza una tabla de buffer, en realidad un cursor actualizable basado en la tabla original. Todos los cambios se han hecho sobre este cursor y sólo se escriben en la tabla cuando se utiliza el comando "update" adecuado.

Estrategias de buffer

La estrategia de buffer determina cuándo y cómo se almacenan en la tabla los cambios que se encuentran en el buffer. Existen tres opciones:

  • No se emplea buffer: Esta vía es solamente una opción para las versiones anteriores a Visual Foxpro versión 3.0 y es además, el comportamiento predeterminado actualmente para las tablas de Visual FoxPro. Cualquier cambio hecho en una tabla va directamente e inmediatamente a la tabla original. No hay posibilidad de "deshacer" sin que se haya programado explícitamente - utilizando Scatter y Gather, por ejemplo. Se establece al asignar "1" como parámetro a CursorSetProp()
  • Buffer de filas: Los cambios no se han enviado a la tabla original a menos que ocurran una de estas dos cosas. O hay una llamada explícita a la función TableUpdate(), o el puntero del registro se mueve dentro de la tabla original. Vea que CUALQUIER movimiento del puntero del registro, aunque sea iniciado por una tabla que esté en buffer de filas, siempre causa un TableUpdate() "implícito". Establezca 2 ó 3 como parámetro para CursorSetProp().
  • Buffer de tabla: Los cambios nunca se envían automáticamente a la tabla original, a menos que exista una llamada a un comando TableUpdate() o TableRevert() siempre debe afectar los cambios que estén guardados en el buffer. Intentar cerrar una tabla con buffer mientras tiene cambios no cometidos provoca que Visual FoxPro genere un error en la versión 3.0; pero este comportamiento cambió en versiones anteriores por tanto, los cambios pendientes simplemente se pierden. (No hay error; ni advertencia de que los cambios se van a perder). Se establece asignando 4 ó 5 como parámetro para CursorSetProp().

Hasta este punto se está preguntando, por qué hay DOS parámetros posibles para cada una de las estrategias que implementan buffer. La respuesta es, como se indica en la introducción, debido a que hay dos estrategias de bloqueo.

Estrategias de bloqueo

Visual FoxPro necesita bloquear el registro físico en la tabla mientras se realizan los cambios en su contenido y existen dos vías con las que se puede establecer el bloqueo automático (opuesto al uso explícito de las funciones RLock()/FLock())

  • Bloqueo pesimista: El registro se bloquea en cuanto un usuario comienza a hacer cambios (El bloqueo en realidad ocurre en cuanto se oprime cualquier tecla válida). Esto evita que cualquier otro usuario pueda hacer o guardar cambios sobre este registro hasta tanto el usuario actual haya completado sus cambios y liberado el registro tanto guardando o revirtiendo los cambios.
  • Bloqueo optimista: Un intento de bloqueo del registro se hace solamente cuando los cambios se comienzan a enviar a la tabla. Esto significa que incluso mientras un usuario hace cambios en el dato, el registro permanece disponible a otros usuarios, los que también podrían, y posiblemente guardarán, cambios en el mismo registro mientras el primer usuario están aun trabajando en el.

Modos de buffer

El modo de buffer para una tabla es, por tanto, la combinación específica de las estrategias de Buffer y Bloqueo que se aplican. Existe un total de 5 modos de buffer para una tabla como se ilustra en la Tabla 1.

Tabla 1. Modos de Buffer en Visual FoxPro

ModoBloqueoBufferComentarios
1PesimistaNingunoÚnica opción para FP2.x, predeterminado para tablas VFP
2PesimistaFilaEl bloqueo se establece por el evento KeyPress. El movimiento del puntero de registro obliga a Guardar
3OptimistaFilaEl bloqueo se establece por TableUpdate(). El movimiento del puntero de registro obliga a Guardar
4PesimistaTablaEl bloqueo se establece por el evento KeyPress. Guardar se debe iniciar explícitamente
5OptimistaTablaEl bloqueo se establece por el evento TableUpdate(). Guardar se debe iniciar explícitamente

Al trabajar con Visual FoxPro, debemos ser cuidadosos al distinguir entre estrategias individuales, que se especifican directamente para Buffer y Bloqueo, y el modo buffer que resulta de la combinación de ellos. Desafortunadamente, como hemos visto, Visual FoxPro es de por sí, menos cuidadoso con esta distinción.

¿Qué significa todo esto cuando creamos formularios enlazados a datos?

He aquí donde las cosas comienzan a ponerse un poco más complejas (y no es solamente por la nomenclatura). Consideremos una situación normal donde las tablas se agregan al formulario por el entorno de datos nativo. El formulario tiene una propiedad llamada "Buffermode" que tiene tres valores posibles:

  • 0 Ninguno (predeterminado)
  • 1 Pesimista
  • 2 Optimista

Vea que esto se refiere en realidad a las opciones para la estrategia de bloqueo y no tiene nada que ver con el buffer ¡¡ para nada !! El hecho por el cual el formulario determina la estrategia buffer es por sus tablas todas por si mismo, basadas en su utilización. Si un formulario utiliza dos tablas que tienen una relación de uno a muchos, muestran el lado "muchos" de la relación de diferentes formas.

Si la tabla "muchos" se utiliza como origen de datos para el control grid, para el formulario, se abre con buffer de tabla. Sin embargo, si la tabla "muchos" se emplea para enlazar con controles que solamente muestran una fila de la tabla cada vez entonces, el buffer para la tabla va a ser lo mismo que para "una" tabla para toda de configuración de la propiedad Buffermode del formulario.

Si crea un pequeño formulario de prueba y ejecuta todas sus opciones para la propiedad Buffermode de un formulario, puede ver que incluso con la propiedad BufferMode establecida en 0-(Ninguna) el cursor creado por Visual FoxPro está aun abierto en modo buffer de fila cuando las tablas se abren desde el entorno de datos del formulario. Es como si para el formulario "No buffer" y "Bufer de filas" fuera lo mismo.

Sin embargo, este NO es el caso si las tablas se abren directamente con el comando USE. Si las tablas se abren explícitamente en el método Load(), entonces la propiedad Buffermode no tiene impacto en lo absoluto, y las tablas se abren en dependencia de los parámetros apropiados en la Sesión de datos. Para formularios que corren en la sesión predeterminada de datos (DataSession = 1) se utilizan los parámetros especificados en la ventana Opciones. Vea que en la ventana Opciones, las opciones, en realidad establecen la configuración del modo de bufer. Tiene las 5 opciones que se corresponden con lo 5 modos definidos antes, en la Tabla 1, y los que utilizan los mismos identificadores que la función CursorSetProp(). Si el formulario se ejecuta en sesión privada de datos, entonces, las tablas abiertas con el comando USE se configuran en dependencia de la configuración establecida para esa sesión de datos y, de forma predeterminada, no serán alojadas en buffer.

¿Aun confundido?

Lo más que puedo decir es que la situación es realmente como sigue:

  • Para las tablas abiertas por el entorno de datos del formulario, no importa si el formulario tiene Datassesion con valor Default o Private. Las tablas siempre tienen almacenamiento en buffer, al menos en modo Optimista de filas.
  • La propiedad BufferMode del formulario en realidad determina la estrategia de bloqueo, no el modo de buffer; pero solo para tablas que han sido abiertas en el entorno de datos del formulario.
  • En la sesión de datos actual, las tablas que han sido abiertas explícitamente tienen ambas estrategias: buffer y bloqueo establecidas en dependencia de la ventana Opciones.
  • En la sesión Privada de datos, las tablas que han sido abiertas explícitamente tienen sus estrategias de buffer y bloqueo establecidas de acuerdo a los parámetros que se han aplicado para esta sesión (Predeterminado = "No Buffering")
  • Estos resultados se pueden verificar estableciendo varias opciones en un formulario sencillo y mostrando el resultado que se obtiene por CURSORGETPROP("BUFFERING")

Entonces, ¿cómo debemos establecer el buffer para un formulario?

La respuesta corta , como siempre, es "depende". Si utiliza el entorno de datos del formulario para abrir las tablas entonces, puede normalmente dejar la opción al Visual FoxPro. En caso contrario puede simplemente emplear la función CursorSetProp() en su código para configurar cada tabla como sea necesario. De cualquier manera necesita ser consciente de las consecuencias para que pueda programar sus actualizaciones rápidamente.

Utilizar BufferModeOverride

La clase dataenvironment posee una propiedad para cada tabla (o, mejor dicho, "cursor") llamado "BufferModeOverride". Esta propiedad va a fijar el modo del buffer para esa tabla (y sólo esa tabla) a una de las estas seis opciones - si, es correcto, SEIS opciones, no cinco - veamos:

  • 0 Ninguno
  • 1 (Predeterminado) Utilizar la configuración del formulario.
  • 2 Búffer pesimista de fila
  • 3 Buffer optimista de fila
  • 4 Buffer pesimista de tabla
  • 5 Buffer optimista de tabla

Existen dos puntos sobre los que hay que prestar atención. Primero, Vea que mientras los números del 2 al 5 se corresponden con los parámetros de CursorSetProp() y son aquellos mismos que están disponibles desde el diálogo Opciones, el valor requerido para la configuración "ninguno" ahora es 0 en lugar de 1. He aquí otra inconsistencia en la configuración del buffer. Segundo, vea que el valor predeterminado para esta propiedad es "1 - Utiliza la configuración de formularios". Cuando se refiere a configuración de formularios, se refiere, por supuesto a la propiedad "BufferMode", la que como ya hemos visto en realidad se encarga de definir la estrategia de bloqueo a aplicar. No existe configuración del formulario para controlar buffer !!.

Habiendo dicho esto, la configuración de la propiedad BufferModeOverride va a asegurar que la tabla se abre utilizando el modo buffer que usted ha especificado. Al menos esta propiedad ha sido nombrada correctamente, sobreescribe todo lo demás y fuerza la tabla a un modo especial de buffer en todas las situaciones.

Utilizar CursorSetProp()

Independientemente de cómo fue abierta la tabla, siempre puede utilizar la función CursorSetProp() para cambiar el modo buffer de una tabla. Sin embargo, si está utilizando el entorno de datos del formulario para abrir sus tablas, entonces, tiene dos opciones en dependencia de si su formulario utiliza sesión privada de datos o sesión actual de datos. En el primer caso, puede establecer simplemente el modo requerido en la ventana Opciones y olvidarse de ello. Todas las tablas se van a abrir siempre con esa configuración y siempre sabrá en qué estado se encuentra. Si utiliza una sesión privada de datos entonces, necesita hacer dos cosas. Primero, necesita asegurarse de que el entorno de datos está configurado para soportar buffer. Algunos parámetros de entorno se limitan a la sesión de datos actual y puede que necesite cambiar el comportamiento predeterminado o alguno, o todos los siguientes (vea el tema SET DATASESSION en el archivo ayuda para una lista completa de parámetros afectados):

  • SET MULTILOCKS - Debe ser ON para permitir buffer, predeterminado OFF
  • SET DELETED - Predeterminado OFF
  • SET DATABASE - "No database" es el predeterminado en una sesión Privada
  • SET EXCLUSIVE - El predeterminado es OFF para una sesión Privada
  • SET LOCK - Predeterminado OFF
  • SET COLLATE - Predeterminado es "MACHINE"
  • SET EXACT - Predeterminado es OFF

Lo segundo que necesita es especificar explícitamente el modo buffer de cada tabla utilizada en la función CursorSetProp() con el parámetro apropiado porque la configuración en la ventana Opciones no se puede aplicar a la sesión privada de datos.

Entonces, ¿qué modo de buffer debo utilizar en mis formulario?

Para nosotros la respuesta es sencilla. Siempre debe utilizar estrategia de buffer de tabla con bloqueo optimista (es decir modo de buffer 5). La razón es simplemente que, con la excepción de creación de un índice, no hay nada que pueda hacer en otro modo que no pueda hacer en este modo. Mientras el buffer de filas puede ser utilizado en desarrollo, no creemos que tienen cabida en una aplicación en funcionamiento. La razón es simplemente que existe muchas formas en las que puede ser desencadenada la función TableUpdate() implícito (causado por el movimiento del puntero de registro) y no todos los que están por debajo de nuestro control directo. Por ejemplo, la función KeyMatch() está definida en el archivo Ayuda, como;

Busca una clave de índice en una etiqueta o un archivo de índice.

Parece suficientemente inofensivo - seguramente buscar un archivo índice no puede causar problemas. Pero una nota (en la sección Observaciones justo al final del tema) indica que:

KEYMATCH( ) devuelve el puntero de registro al registro donde estaba originalmente antes de ejecutar KEYMATCH( ).

Aquí se bloquea! Seguramente "devuelve el puntero de registro" implica que mueve el puntero del cursor - lo que en efecto - hace. La consecuencia es que si está utilizando el buffer de filas y quiere verificar por clave duplicados utilizando keymatch(), puede realizar inmediatamente cualquier cambio pendiente. (Por supuesto, en la versión 6 o después pueden siempre utilizar en su lugar IndexSeek(). Sin embargo, el mismo problema surge cuando muchos de los comandos y funciones que operan sobre una tabla - especialmente el más viejo introducido en FoxPro antes los días de buffer (e.g. CALCULATE, SUM y AVERAGE).

Aun más importante, el hecho de que configure una tabla para buffer de tabla no lo previene de que la tabla como si en realidad hubiera buffer de filas. Ambas funciones TableUpdate() y TableRevert(). Esto puede significar que tiene que escribir un poco más de código; pero puede evitar muchos problemas.

Cambiar el modo de buffer de la tabla

Hemos dicho, al inicio de la última sesión, que puede utilizar siempre CursorSetProp() para establecer o cambiar el modo de buffer de una tabla. Esto es cierto; pero si la tabla ya ha tenido asignado un modo buffer, puede no ser tan sencillo porque al cambiar el estado de buffer forzaría a Visual FoxPro a verificar el estado de cualquier buffer existente.

Si la tabla tiene buffer de filas, y tiene cambios no confirmados, Visual FoxPro los envía y permite cambiar el modo. Si el destino tiene buffer de tablas, y trata de cambiar su modo de buffer mientras existen cambios no confirmados, Visual FoxPro se queja y manda el error 1545 ("El búfer de tablas para el alias "nombre" contiene cambios no confirmados"). Esto es un problema porque no puede indexar una tabla, o hacerla libre una tabla o cursor transactable, los que mientras la tabla tiene buffer, por tanto es la única vía de hacer estas cosas, temporalmente, a buffer de filas. Por supuesto, la solución es siempre suficiente - asegúrese sólo de que no hay cambios pendientes antes de que intente cambiar el modo de buffer.

2 comentarios :

Los comentarios son moderados, por lo que pueden demorar varias horas para su publicación.