Implementando transacciones a nivel de ap licación

¿Cuál consideráis la mejor manera de implementar una transacción a
nivel de controlador?

Lo que entiendo por transacción es ejecutar un unas determinadas
acciones de forma atómica, y que, en el caso de que no se ejecuten se
vuelva al estado anterior.

Yo he pensando en un

begin
accion_1
accion_2
accion_n
rescue
deshago_las_acciones
end

pero no sé si es el modo más adecuado. También me suena que los
bloques permiten este tipo de atomicidad, pero claro, manejar la
excepción me permite restaurar el estado anterior , y un bloque no.

Un saludo y gracias.

On 1/27/07, Fernando B. [email protected] wrote:

accion_1
Un saludo y gracias.

Las transacciones están soportadas en Rails. Lo único que tienes que
hacer es Modelo.transaction do … end. En ese bloque cualquier
excepción provoca un rollback de la transacción (pero cuidado que la
excepción se sigue propagando, por lo que hay que hacer un rescue,
aunque no restaurar los datos), por lo que es necesario utilizar
métodos con la exclamación al final (“save!” en vez de “save”).

Las transacciones, a pesar de tener que declararse Modelo.transaction
son propias de la conexión, por lo que afectan a todas las tablas de
esa conexión… y otra cosa, en el rescue es recomendable hacer un
reload de los modelos que hayas podido modificar, ya que parece que a
pesar de que no cambián en la base de datos, los datos de Rails no
parecen funcionar igual…

Os agradezco la respuesta, pero me temo que me he expresado mal: si os
fijáis en el asunto de mi e-mail he puesto “a nivel de aplicación”,
queriendo decir realizar una transacción en el controlador pero no
sobre el modelo de datos, sino a nivel de
aplicación.
Más concretamente lo que me interesa es un tema de que el borrado y la
generación de la misma caché se realice de forma atómica.

Así que no se trataba de las transacciones de AR, no.

Pero gracias :slight_smile:

Las transacciones, a pesar de tener que declararse Modelo.transaction
son propias de la conexión, por lo que afectan a todas las tablas de
esa conexión…
En realidad no tienes porqué declarar las transactions a nivel de
modelo. Declararlas a partir del modelo queda muy claro cuando estás
trabajando sólo con un modelo, pero transaction es un método de
ActiveRecord::Base (del que extienden los modelos), así que puedes
utilizar

ActiveRecord::Base.transaction do
      acciones
end

A mí me resulta mucho menos confuso usar esto cuando hay varios modelos
en juego en la transaction.

y otra cosa, en el rescue es recomendable hacer un
reload de los modelos que hayas podido modificar, ya que parece que a
pesar de que no cambián en la base de datos, los datos de Rails no
parecen funcionar igual…
Esto también puedes conseguirlo automáticamente, aunque en la 1.2 lo han
“deprecado”. Para hacer eso la sintáxis es
ActiveRecord::Base.transaction(objeto1,objeto2,objeto3…) do
acciones
end

En este caso al hacer rollback se devuelven los objetos especificados a
su estado anterior, con el inconveniente de que toda su información
adicional (por ejemplo, el resultado de las validaciones) se pierde
también, porque hace rollback del estado completo del objeto. Por ese
motivo lo han sacado de la 1.2 aunque si es necesario sigue disponible
como plugin.

Como nota adicional, esto funciona solamente para una sola conexión. Si
mi transacción usa modelos de diferentes conexiones, AR no es capaz de
trazarlo.

Saludos,

javier ramírez

fijáis en el asunto de mi e-mail he puesto “a nivel de aplicación”,queriendo decir realizar una transacción en el controlador pero no sobre el modelo de datos, sino a nivel de aplicación.
la verdad es que vi tu mail y la respuesta y pensé que eran transacciones
de db. Esto me pasa por no leer :wink:

Más concretamente lo que me interesa es un tema de que el borrado y la generación de la misma caché se realice de forma atómica.

La solución que planteas de encerrar en un begin/rescue/end no me parece
mala, y así a bote pronto no se me ocurre otra forma. Siempre puedes
cambiar el begin por un transaction do y por el mismo precio te llevas
el rollback de base de datos también hecho.

Yo estuve dándole vueltas hace poco a un problema de hacer rollback de
algunas llamadas a métodos de forma automática, pero era en una parte
pequeña de la aplicación y de forma controlada. De hecho no llegué a
implementarlo, aunque no descarto ponerme un día de estos. Era pensando
en hacer migrations un poco más tolerantes a fallos, y lo que se me
ocurría era que todos los métodos que hacían una acción y los métodos
que deshacían esa misma acción compartían el mismo nombre, pero con un
prefijo/sufijo distinto (en mi caso up y down por estar en las
migrations).

Mi plan era al capturar una excepción leer el stack de llamadas de la
aplicación y ver qué métodos se habían ido ejecutando, para llamar en
orden inverso al método contrario (si era un método up llamaría al down,
y si era un down llamaría al up). De esta forma mi rescue sólo tendría
una llamada a “migration_rollback” y no tengo que estar siempre
añadiendo al rescue el procedimiento de back de cada acción, sino que lo
selecciona automáticamente a partir del stack.

Además, esto me permite ejecución condicional. Si no he pasado por un
método concreto no le hago el rollback. De otro modo en el rescue no
tendría forma de saber qué partes tengo que echar atrás y cuáles no.

Si leer del stack me diese muchos problemas (o si no me dejase ir atrás
lo suficiente), había pensado en utilizar set_trace_func y mantener yo
mismo una pila de llamadas en memoria.

De todos modos, como te decía esta idea la tenía pensada para una parte
pequeña y controlada de la aplicación, donde que todos los métodos
tengan su contrario distinguiéndose por nombre es bastante asequible y
donde las operaciones que se están haciendo son limitadas. Y, lo más
importante, no la he puesto en práctica así que puede tener sus pegas :wink:

saludos,

javier ramírez

Gracias Javier por tu respuesta.

La verdad es que lo que tú planteas no requiere tanta sofisitcación en
mi caso, ya que símplemente me tengo que asegurar que la caché se haya
borrado, si o sí, y ya
está.
Un saludo!