Concurrencia y transacciones - script - ejemplo

From Ibbddunq

Contents

El esquema de BD

  encomienda <nroEncomienda, cliente, peso>
  servicio <nroServicio, origen, destino, disponible, cargaTotal>
  encomiendaEnServicio <nroServicio, nroEncomienda>
  

agregarEncomienda

  procedure agregarEncomienda(elCliente, elPeso, elServicio)
  begin
    declare nroNuevaEnco integer;
    declare disponibleNuevo integer;
    declare cantEncomiendas integer;
    
    select max(nroEncomienda) + 1 from encomienda into nroNuevaEnco;
    select disponible - elPeso from servicio
     where nroServicio = elServicio
      into disponibleNuevo;
    select count(*) from encomiendaEnServicio where nroServicio = elServicio
      into cantEncomiendas;
    
    if (cantEncomiendas < 10) then
        insert into encomienda (nroEncomienda, cliente, peso)
        values (nroNuevaEnco, elCliente, elPeso);
        insert into encomiendaEnServicio (nroEncomienda, nroServicio)
        values (nroNuevaEnco, elServicio);
        update servicio set disponible = disponibleNuevo
         where nroServicio = elServicio;
    end if;
  end
  

con este stored procedure se puede

  • mostrar un schedule de dos tx en el que el disponible de un servicio quede mal.
  • llegar a la conclusión de qué condición deben tener dos ejecuciones concurrentes para que pueda haber anomalías de concurrencia: que estén agregando encomiendas en el mismo servicio.
  • ver cómo tiene que ser un schedule para ser "malo": ambos deben leer el disponibleNuevo antes de que cualquiera de ellos haga update. Ver que si es serial, entonces naturalmente es "bueno", el problema está relacionado con cruces.
  • corregir el update seteando disponible - elPeso, y sacando el select del servicio de arriba. Aclarar que toma el disponible en el momento que se ejecuta el update. Llegar a la conclusión de que se arregla la anomalía observada antes ...
  • ... pero queda otra: puedo poner 11 encomiendas en un servicio. Esta la vamos a tener que solucionar reservando un recurso, o sea, que sea válido que yo diga "yo agarro un servicio y hasta que yo no termine nadie puede hacer nada con el servicio"


pasarPeso

   procedure pasarPeso (servDesde, servHacia, cuanto)
   begin
     declare nuevoDisponible;
   
     start transaction
     update servicio set disponible = disponible + cuanto 
      where nroServicio = servDesde;
     select disponible - cuanto from servicio 
      where nroServicio = servHacia
       into nuevoDisponible;
     
     if (nuevoDisponible >= 0) then
         update servicio set disponible = disponible - cuanto
          where nroServicio = servHacia;
         commit;
     else
         rollback;
     end if;
   end

Un ejemplo armadito: supongamos esta instancia de servicio (considerando solamente nro y peso)

  104, 500
  123, 200
  148, 33000
  149, 50
  150, 820

Veamos posibles transacciones concurrentes

  pasarPeso(desde 150, hacia 104, 320) con pasarPeso(desde 148, hacia 104, 400)

en este caso el 104 me puede quedar con disponible negativo, p.ej. ver este schedule

   150 --> 104 - 320                 148 --> 104 - 400
       start
       update del 150
       select del 104 (da 180)
                                       start
                                       update del 148
                                       select (da 100)
       if
       update del 104
          (deja 180)
       commit
       fin
                                       if
                                       update del 104 (deja -220)
                                       commit
                                       fin

obsérvese que en la 2da transacción, el valor de disponible que leyó el select no coincide con el que tomó el update, o el que tomaría en un select puesto en el mismo lugar del update. Esa diferencia es el non repeatable read.

  pasarPeso(desde 123, hacia 149, 120) con pasarPeso(desde 150, hacia 123, 240)

en este caso el 104 me puede quedar con disponible negativo, p.ej. ver este schedule

   123 --> 149 - 120                150 --> 123 - 240
       start
       update del 123 a 320
       select (da -70)
                                       start
                                       update del 150
                                       select del 123 
                                          (da ... ¿cuánto? ponele 80)
                                       if
                                       update del 123 ... debe esperar
       if
       rollback, 
         deja 200 en el 123
       fin
                                       ahora sí update ... oops a -40
                                       commit
                                       fin

sí, el rollback puede traer efectos cruzados extraños ... más sobre esto en SO. En IBD nos alcanza con ver que hay un problema, y con resolverlo en forma robusta usando lockeos.

(si da)

  pasarPeso(desde 123, hacia 149, 20) con pasarPeso(desde 149, hacia 123, 40)

este hacerlo cuando ya se haya hecho el lockeo

   123 --> 149 - 120                149 --> 123 - 240
       start
       update del 123 
                                       start
                                       update del 149
       select for update
         del 149 ... debe esperar
                                       select for update del 123 
                                          ... deadlock


cantidadServiciosADestinos

function cantidadServiciosADestinos(dest1, dest2) begin

   declare cantServ1, cantServ2;
   
   select count(*) from servicio where destino = dest1 into cantServ1;
   select count(*) from servicio where destino = dest2 into cantServ2;
   return cantServ1 + cantServ2;

end

ponele esta instancia de servicio (sólo nroServicio, origen, destino)

   104, La Plata, Brandsen
   148, Ranchos, Chascomus
   150, La Plata, Chascomus
   151, La Plata, Chascomus

y tengo dos tx concurrentes

   cantidadS...(Brandsen,Chascomus)
   primer select --> 1
                                  update servicio set destino = 'Brandsen'
                                  where nroServicio in (150,151);
   segundo select --> 1
   return --> ¡¡2!!
   

lo mismo con

   cantidadS...(Chascomus,Brandsen)

da 6.

Personal tools