Evitando que un usuario se conecte más de una vez (método mejorado)

10 comentarios

En este artículo ya habíamos visto un método para evitar que los usuarios se conecten más de una vez a la Base de Datos:

Evitando que un usuario se conecte más de una vez a la Base de Datos

pero allí podríamos tener un pequeño problema si la computadora donde se encuentra el Servidor del Firebird se apaga incorrectamente, dejando por lo tanto nombres de usuarios en la tabla CONECTADOS. Esos usuarios evidentemente cuando se reinicia el Servidor no estarán conectados pero en la tabla CONECTADOS dirá que sí lo están. Una solución sería que un usuario quien no estaba conectado borre todas las filas de esos usuarios en la tabla CONECTADOS. Funcionará bien, pero, hmmmmmmm, no es profesional, ¿verdad?

Aquí hay otro método para borrar a esos usuarios “fantasmas”. Son “fantasmas” porque la tabla CONECTADOS nos indica que están conectados a la Base de Datos pero la realidad es que no lo están.

Lo que haremos será agregarle una columna a la tabla CONECTADOS y en esa columna guardaremos el identificador de la conexión. Luego, en nuestro trigger antes de intentar la inserción borraremos a todos los usuarios cuyos identificadores de conexión no se encuentren en la tabla CONECTADOS y también en la tabla MON$ATTACHMENTS.

En la tabla del sistema MON$ATTACHMENTS se guardan los datos de todas y cada una de las conexiones actuales. Uno de esos datos es el identificador de la conexión.

Entonces, la estructura de nuestra tabla CONECTADOS ahora sería la siguiente:

CONECTADOS1

Captura 1. Si haces clic en la imagen la verás más grande

Como puedes ver en la Captura 1. a la tabla CONECTADOS se le agregó la columna CON_IDECON, para guardar en esa columna el identificador de la conexión (ese identificador lo encontraremos en la tabla MON$ATTACHMENTS y para la conexión actual siempre será igual a CURRENT_CONNECTION).

El trigger para insertar un usuario a la tabla CONECTADOS

Nuestro trigger ahora cambiará un poco, quedará así:

Listado 1.

CREATE TRIGGER INSERTAR_CONECTADO
   ACTIVE ON CONNECT
   POSITION 2
AS
BEGIN

   -- Primero, se borran las filas de los usuarios que no están conectados
   -- pero aparecen como conectados

   DELETE FROM
      CONECTADOS
   WHERE
      CON_IDECON NOT IN (SELECT MON$ATTACHMENT_ID FROM MON$ATTACHMENTS);

   -- Segundo, se trata de insertar una fila a la tabla CONECTADOS.
   -- Como la tabla CONECTADOS tiene una restricción UNIQUE KEY, un usuario
   -- no se podrá conectar más de una vez al mismo tiempo

   INSERT INTO CONECTADOS
              (CON_IDENTI, CON_IDECON , CON_NOMBRE , CON_TIMEST)
       VALUES (0 , CURRENT_CONNECTION, CURRENT_USER, CURRENT_TIMESTAMP);

END;

¿Qué hace este trigger?

Primero, borrará todas las filas de la tabla CONECTADOS cuyo valor de CON_IDECON no coincida con algún valor de ATTACHMENT_ID de la tabla ATTACHMENTS. En otras palabras, en la tabla CONECTADOS quedarán solamente los nombres de los usuarios que efectivamente están conectados a la Base de Datos, no tendrá filas “fantasmas”.

Segundo, intentará insertar una fila a la tabla CONECTADOS. Si el nombre del usuario que intenta la conexión no existe en esa tabla entonces podrá continuar pero si existe entonces ocurrirá una excepción y se evitará la conexión.

El trigger para borrar un usuario de la tabla CONECTADOS

Este trigger quedará exactamente igual al que habíamos visto en el artículo anterior, no tiene por qué cambiar.

Listado 2.

CREATE TRIGGER BORRAR_CONECTADO
   ACTIVE ON DISCONNECT
   POSITION 3
AS
BEGIN

   DELETE FROM
      CONECTADOS
   WHERE
      CON_NOMBRE = CURRENT_USER;

END;

Conclusión:

Para evitar que un apagado anormal del Servidor del Firebird deje usuarios “fantasmas” en la tabla CONECTADOS podemos tener en esa tabla una columna que nos indique cual es el identificador de cada conexión. Así, antes de intentar insertar los datos de un usuario en la tabla CONECTADOS borramos las filas de esa tabla que no tengan una conexión activa. Lo que conseguiremos será que la tabla CONECTADOS siempre nos muestre los nombres de los usuarios que efectivamente se encuentran conectados.

Artículos relacionados:

Limitando la cantidad de usuarios conectados

Evitando que un usuario se conecte más de una vez a la Base de Datos

El índice del blog Firebird21

El foro del blog Firebird21

Sobre conexiones y desconexiones

7 comentarios

¿Cuándo conectarse y cuándo desconectarse de la Base de Datos?

Aquí tenemos varias posibilidades, analizaremos lo que implica cada una de ellas:

  1. Conectarse cuando la aplicación (Contabilidad, Facturación, Ventas, Sueldos, etc.) inicia y desconectarse cuando la aplicación finaliza
  2. Conectarse cuando el formulario se inicia y desconectarse cuando finaliza
  3. Conectarse para realizar una tarea y desconectarse cuando la tarea finaliza

¿Cuál elegir?

En general y en casi todos los casos la mejor opción es la número 1. ¿Por qué? porque tener una conexión abierta no puede causarle daño a la Base de Datos y eso de estar abriendo y cerrando conexiones sin necesidad solamente acarreará pérdida de tiempo. Abrir una conexión o cerrar una conexión no es instantáneo, toma unos cuantos milisegundos hacerlo. Puede parecer muy poco pero si lo multiplicas por la cantidad de veces que un usuario lo hace en un día y a ese resultado lo multiplicas por la cantidad de usuarios y a ese resultado lo multiplicas por la cantidad de días laborables del mes puedes encontrarte con muchos minutos o inclusive muchas horas desperdiciadas por estar abriendo y cerrando conexiones sin necesidad.

Pero hay casos en los que sí es conveniente abrir_conexión, realizar_tarea, cerrar_conexión ¿cuáles son esos casos? cuando la señal es débil o es mala o siempre se realiza por Internet. Por ejemplo si alguien está viajando y se conecta al Servidor de la empresa desde su notebook (también llamada laptop) y sufre frecuentes cortes de Internet. O cuando la conexión siempre es a través de Internet (el caso de las aplicaciones “en la nube”).

Pero cuando se trata de una red local que funciona bien no tiene sentido eso de abrir_conexión, realizar_tarea, cerrar_conexión porque ningún beneficio se obtiene de ello y sí perjuicios: por un lado pérdida de tiempo como ya habíamos visto y por el otro que no se podrían usar tablas GTT de conexión, solamente se podrían usar tablas GTT de transacción.

Conclusión:

Si la conexión a la Base de Datos es a través de una red local entonces siempre lo más conveniente es usar la opción 1.

La opción 3 suele ser la más conveniente cuando los cortes de conexión son frecuentes, eso podría suceder cuando alguien está viajando y usando una notebook para conectarse a la Base de Datos que se encuentra en el local de la empresa.

También habría que usar la opción 3. cuando los usuarios siempre o mayormente se conectan por Internet a la Base de Datos.

Lo mejor técnica entonces es que desde tu aplicación el usuario pueda elegir el método de conexión. Aquellos usuarios que lo hacen a través de una red local deberían tener habilitada la opción 1. y aquellos que lo hacen a través de Internet deberían tener habilitada la opción 3. Y siempre deberían tener la posibilidad de cambiarse de la una a la otra.

Artículo relacionado:

El índice del blog Firebird21

Los triggers de la Base de Datos

4 comentarios

Desde Firebird 2.1 tenemos la posibilidad de usar triggers de la Base de Datos.

¿Qué es un trigger de la Base de Datos?

Un trigger que se lanza cuando ocurre una de estas condiciones:

  • CONEXIÓN a la Base de Datos
  • DESCONEXIÓN de la Base de Datos
  • INICIO de una Transacción
  • COMMIT de una Transacción
  • ROLLBACK de una Transacción

¿Quiénes pueden crear, modificar o borrar triggers de la Base de Datos?

Solamente el usuario SYSDBA y el creador de la Base de Datos pueden hacerlo

¿Para qué pueden usarse los triggers de la Base de Datos?

  • Para saber quienes se conectaron
  • Para saber quienes se desconectaron
  • Para evitar que se conecten desde una dirección IP
  • Para evitar que se conecte un usuario
  • Para evitar que se conecten fuera de los días establecidos
  • Para evitar que se conecten fuera del horario establecido
  • etc.

Ejemplo 1:

CREATE TRIGGER RegistrarConexion
   ON CONNECT
AS
BEGIN
   INSERT INTO CONEXIONES(CON_USUARI  , CON_TIEMPO, CON_MENSAJ)
                   VALUES(CURRENT_USER, CURRENT_TIMESTAMP, 'Conectado');
END

Este trigger registrará todas las conexiones de los usuarios, con sus fechas y horas, y así podremos saber fácilmente quienes se conectaron y cuando lo hicieron.

Ejemplo 2:

CREATE TRIGGER RegistrarDesconexion
   ON DISCONNECT
AS
BEGIN
   INSERT INTO CONEXIONES(CON_USUARI , CON_TIEMPO, CON_MENSAJ)
      VALUES(CURRENT_USER, CURRENT_TIMESTAMP, 'Desconectado');
END

Con este trigger registramos cada vez que un usuario se desconecta de la Base de Datos. Junto con el anterior nos permitirá saber cuando cada usuario estuvo conectado, si estuvo conectado fuera de las horas laborales, etc.

Ejemplo 3:

CREATE EXCEPTION
   E_ACCESONOPERMITIDO 'Tu dirección de IP no tiene permiso para conectarse a esta Base de Datos' ;

CREATE TRIGGER RegistrarConexion
   ON CONNECT
AS
BEGIN
   IF (EXISTS(SELECT 1 FROM MON$ATTACHMENTS M WHERE M.MON$ATTACHMENT_ID = CURRENT_CONNECTION AND M.MON$REMOTE_ADDRESS = '192.168.0.100')) THEN
      EXCEPTION E_ACCESONOPERMITIDO;
END

En este ejemplo se rechazan todos los intentos de conexión desde la dirección IP 192.168.0.100, nadie podrá conectarse desde esa computadora

Ejemplo 4:

CREATE EXCEPTION
   E_USUARIONOPERMITIDO 'Tú no puedes conectarte a esta Base de Datos, no insistas' ;

CREATE TRIGGER RegistrarConexion ON CONNECT
AS
BEGIN
   IF (EXISTS(SELECT 1 FROM MON$ATTACHMENTS M WHERE M.MON$ATTACHMENT_ID = CURRENT_CONNECTION AND CURRENT_USER = 'JUAN')) THEN
      EXCEPTION E_USUARIONOPERMITIDO;
END

En este ejemplo le impedimos al usuario Juan conectarse, por más que insista no lo logrará.

Ejecución de los triggers de la Base de Datos y el manejo de las excepciones

Los triggers CONNECT y DISCONNECT son ejecutados en una transacción creada específicamente para ellos.

Si todo va bien entonces esa transacción es confirmada (finaliza con un COMMIT) automáticamente. Punto.

Pero si ocurrió una excepción y no fue atrapada entonces se hace el ROLLBACK de la transacción y:

  • En el caso de CONNECT, la conexión es desechada y la excepción se devuelve al Cliente
  • En el caso de DISCONNECT, la conexión es desechada pero la excepción no se devuelve al Cliente

Los triggers de la TRANSACCIÓN son ejecutados dentro de la misma transacción. La acción tomada cuando ocurre una excepción que no fue atrapada depende del tipo de trigger:

  • En un trigger START, la excepción es informada al Cliente y la transacción es desechada
  • En un trigger COMMIT, la excepción es informada al Cliente, las acciones del trigger son desechadas y la grabación es cancelada
  • En un trigger ROLLBACK, la excepción no es informada al Cliente y la transacción es desechada

De lo anterior podemos notar que no hay forma de saber si un trigger DISCONNECT o un trigger ROLLBACK causó una excepción, porque ninguno de ellos se lo informa al Cliente.

También habrás notado que no podremos conectarnos a una Base de Datos si fue el trigger CONNECT el que causó la excepción ni podremos iniciar una transacción si fue el trigger TRANSACTION START el que causó la excepción. Ambos triggers nos impiden que podamos utilizar a la Base de Datos y por lo tanto no podemos solucionar el problema.

¿Cómo se puede evitar que se disparen los triggers de la Base de Datos?

Para que podamos solucionar los errores que hayamos cometido en los triggers CONNECT y TRANSACTION START algunos de los programas que vienen incluidos con el Firebird tienen nuevas opciones:

GBAK -nodbtriggers
ISQL -nodbtriggers
NBACKUP -T

Estas opciones solamente pueden ser usadas por el usuario SYSDBA o por el creador de la Base de Datos.

Si iniciamos el programa ISQL con la opción -nodbtriggers entonces los triggers de la Base de Datos no serán ejecutados, podremos conectarnos a esa Base de Datos, revisar el código fuente de los triggers y solucionar el error que cometimos.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21