Re: Bloqueo en registro-tabla

From: Jaime Casanova <jcasanov(at)systemguards(dot)com(dot)ec>
To: Mariano Reingart <mariano(at)nsis(dot)com(dot)ar>
Cc: suso <jlcubas(at)terra(dot)es>, pgsql-es-ayuda(at)postgresql(dot)org
Subject: Re: Bloqueo en registro-tabla
Date: 2009-06-16 04:32:21
Message-ID: 3073cc9b0906152132k3419abf1l8d5765f38be79dcc@mail.gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-es-ayuda

2009/6/15 Mariano Reingart <mariano(at)nsis(dot)com(dot)ar>:
>
> Al empezar, haces:
> SELECT *, xmin FROM historia_clinica WHERE numero=1234;
> -- guardas el xmin y empieza la edición (por ej, en variable xmin0)
>
> -- al guardar, comienzo una transacción
> BEGIN;
> -- selecciono el xmin actual y bloqueo la tabla para que otro no pueda
> modificar ese registro
> SELECT xmin FROM historia_clinica WHERE numero=1234 FOR UPDATE;
> -- si xmin != xmin0 lanzar un error porque los datos se modificaron
> (ROLLBACK)
> -- si xmin == xmin0, hacer el UPDATE y COMMIT
>
[...]
>
> BEGIN;
> SELECT fecha_bloqueo+CAST('24 hours' AS INTERVAL)<now() AS bloqueado FROM
> historia_clinica WHERE numero=1234 FOR UPDATE;
> -- si bloqueado es TRUE, todavia no paso el periodo de 24hs), hacer ROLLBACK
> e informar al usuario que el registro esta bloqueado.
> -- si bloqueado es NULL o falso (por ej., pasaron 24hs), pones una nueva
> fecha de bloqueo (que comienza ahora):
> UPDATE historia_clinica SET fecha_bloqueo=now() WHERE numero=1234;
> COMMIT;
>

para que usas el xmin en este enredo?
solo necesitas grabar la fecha y al menos el usuario (y quizá la ip de
la máquina cliente)

-- esta transaccion es obviamente pseudocodigo...
BEGIN;
SELECT * FROM historia_clinica WHERE numero=1234 FOR UPDATE NO WAIT;
-- si no se puede bloquear alguien lo tiene bloqueado, ROLLBACK
-- probablemente puedes agregar la condición del IF en el SELECT
IF historia_clinica.fecha_bloqueo IS NULL OR
historia_clinica.usuario_bloqueo = current_user THEN
UPDATE historia_clinica SET fecha_bloqueo = now(), usuario_bloqueo
= current_user
WHERE numero = 1234;
END IF;
COMMIT;

crea un trigger en la tabla historia_clinica que sea mas o menos asi:

CREATE FUNCTION verifica_bloqueo() RETURNS TRIGGER AS $$
BEGIN
IF old.fecha_bloqueo IS NULL THEN
RETURN NEW;
END IF;

IF old.usuario_bloqueo = current_user THEN
-- yo mismo lo habia bloqueado, asi que puedo continuar
RETURN NEW;
END IF;

-- si lo tenia bloqueado alguien mas, chequeo cuando lo
bloqueo para saber si puedo ignorar ese bloqueo.
-- ojo que now() usa la fecha de inicio de la transaccion,
otras opciones en
-- http://www.postgresql.org/docs/8.3/static/functions-datetime.html#FUNCTIONS-DATETIME-CURRENT
IF old.fecha_bloqueo + CAST('90 minutes' AS INTERVAL) < now() THEN
RETURN NEW;
END IF;
RAISE EXCEPTION 'registro bloqueado.'
RETURN NULL;
END;
LANGUAGE plpgsql;

-- la razon por la que lo creo con el 1 adelante es para que se
ejecute primero (los triggers se ejecutan en orden alfabético,
-- me parece que el 1 esta antes de las letras) para que no pierda el
tiempo haciendo otras verificaciones que podrian
-- no servir si el registro estaba bloqueado
CREATE TRIGGER 1_trg_verifica_bloqueo BEFORE UPDATE ON historia_clinica
FOR EACH ROW EXECUTE PROCEDURE verifica_bloqueo();

ahora si desconectate todo lo que quieras... el FOR UPDATE evitara que
dos usuarios bloqueen logicamente el mismo registro a la vez y el NO
WAIT es para que de un error si alguien ya tenia un bloqueo físico en
el registro en lugar de simplemente esperar al COMMIT, y luego el
trigger se asegurara que nadie modifique un registro bloqueado
lógicamente

vaya complicación!!!
cuéntame que vas a hacer el día que un medico deje bloqueado un
registro y una vez que el se vaya a su casa (10 minutos después) otro
medico necesite modificar ese registro pero como esta bloqueado le
tocara esperar 80 minutos!!! (y claro como con todo usuario necesita
modificarlo de urgencia, no puede esperar 80 minutos, ni 10 ni nada)

> Obviamente cuando el usuario modifica definitivamente el registro, tenes que
> establecer la fecha de bloqueo a NULL para liberar el registro,

y el usuario_bloqueo claro

>
> Se podría ir más lejos con triggers para evitar que otro usuario no pueda
> escribir en el mismo registro (haciendo un bloqueo "más físico" y no tan
> lógico), pero esto ya sería mucho más complejo.
>

en realidad me parece mas simple...

ahora, insisto en que es demasiada complicación solo por el capricho
de quererse desconectar sin aun saber si vas a tener algún problema
realmente manteniendo la conexión durante toda la transacción.
y creeme 90 minutos es un tiempo exagerado, posiblemente lo escogieron
de forma arbitraria.

--
Atentamente,
Jaime Casanova
Soporte y capacitación de PostgreSQL
Asesoría y desarrollo de sistemas
Guayaquil - Ecuador
Cel. +59387171157

In response to

Responses

Browse pgsql-es-ayuda by date

  From Date Subject
Next Message Dilm E.I.R.L 2009-06-16 05:00:15 Re: Backup
Previous Message Dilm E.I.R.L 2009-06-16 04:17:47 Re: Backup