Consulta sobre bloqueos

Francesc E. wrote:

On Jul 14, 2008, at 9:45 PM, Xavier N. wrote:

Por que es una cola solucion a este problema?

Cada vez que alguien carga una p�gina se contabiliza y se guarda en la
base de datos. Eso no deberia ser un problema, pero Fernando C.
tiene problemas con los bloqueos de la base de datos.

Entre las opciones que se le han dado, yo coment� tener una cola donde
se fueran contabilizando todas estas consultas y que cada X minutos se
fuera procesando. Quizas una cola no es una soluci�n a este problema,
pero es una opci�n para solucionarlo. �No?

Yo hice el comentario, Fernando B. coment� como lo hacen en La
Coctelera, y yo he continuado hablando de colas.

Lo que seria interesante saber es que tipo de tablas est� utilizando,
como est� el servidor de carga, etc …

Respecto al tipo de tablas, ya lo comentaba arriba:

En este caso el bloqueo es a nivel de fila, es Innodb, pero en una lista
en las que hay que consultar muchas filas la probabilidad de colisión no
es lo suficientemente baja como para darla por aceptable…

El servidor es un dedicado de gama alta con CentOS, va bastante sobrado;
no es problema de exceso de carga, sino de concurrencia puntual. Una
colisión muy improbable a nivel de un registro, pero no tanto a nivel de
una lista…

s2 y gracias… y por cierto, creo que aunque nos hayamos ido un
poquillo del tema, no deja de ser un hilo muy interesante!!

Yo no se mucho de bases de datos, pero creo que si a base de F5s
podemos obtener un 500 y no hay un :lock => true explicito en el
codigo (sin tratar debidamente) es que hay algo mal en algun lugar.

MySQL es un servidor y puede ocurrir que los datos no queden
consistentes (por ejemplo que dos transacciones se pisen y en lugar de
un +2 veamos un +1), puede ser que tarde en responder si estamos
lanzando un monton de peticiones concurrentes, pero petar lo veo raro.

Si algo tan simple pudiera dar lugar a un pete de MySQL deberiamos
estar enviando todo AR por un sistema de colas!

Por ejemplo, un script que estresa mas como este:

Foo.delete_all
Foo.create(:n => 0)
30.times do
  fork do
    puts $$
    Foo.establish_connection # one connection per process
    100.times do
      foo = Foo.first
      foo.update_attribute(:n, foo.n + 1)
    end
  end
end
Process.waitall

No peta. Naturalmente n no termina siendo 3000 ni de lejos, pero petar
no peta.

On Jul 14, 2008, at 10:18 PM, Fernando C. wrote:

Entiendo que la idea es que ya que se van a hacer muchísimas
actualizaciones, que puntualmente provocarán colisiones con lecturas
de
forma más o menos aleatoria… si se pueden agrupar las
actualizaciones,
y llevarlas a cabo en un momento de menor carga, se reduce la
probabilidad de colisiones.

¿Has calculado cuantos INSERTS/SELECTS/UPDATES por segundo aguanta tu
base de datos?

Me he releido al primer email que enviaste comentando el problema, y
he intentado reproducir el error que comentas, pero ahora no pasa. No
serà un problema con la memoria del servidor, procesador … no se,
por decir algo. Que te dice el log de Rails. Tienes algun sistema de
logging activado en MySQL?

De todos modos, está claro que meter una tabla específica con un
registro por cada página vista sería una solución más efectiva, pues
según lo veo yo la cola amortiguaría pero no resolvería del todo el
problema; pero falta ver si eso no sería un poco “matar moscas a
cañonazos”…

Meter una tabla específica tambien te serviria para controlar las
visitas a una pagina por IP por ejemplo.

Aunque no tengo claro si realmente, mediante colas, se puede conseguir
una solución al 100% del problema, o sólo una mejora (como yo veo).

Puedes intentar reproducir tu el error y mandar el log?

On Jul 15, 2008, at 12:56 AM, Xavier N. wrote:

Yo no se mucho de bases de datos, pero creo que si a base de F5s
podemos obtener un 500 y no hay un :lock => true explicito en el
codigo (sin tratar debidamente) es que hay algo mal en algun lugar.

En el production.log te aparece algun “ActiveRecord::StatementInvalid
(database is locked …)”?

¿Por casualidad no estaras utilizando ferret o tirando algun proceso
largo con un “after_update”?

Pero Xavier, ahí tendrías que lanzar un hilo en paralelo que estuviera
realizando SELECT sobre la misma fila para replicar el escenario
exacto.

Por otro lado contenstando a dos mensajes de arriba:

@Francesc: en este caso sí que es petar la base de datos porque 500
cargas son 500 inserts. Nosotros lo tenemos en La Coctelera para
quitar carga a observers y sweepers, lo que a lo mejor supone 20
inserts por minuto. Y tener el respaldo de la base de datos mola
mucho.

Aunque Starling también tiene que molar todo.

@Xavier: un sistema de colas no me parece la solución, sinceramente
porque al final los INSERT los vas a hacer igual, y además tendrás que
hacer DELETE de las tareas concluídas y el respectivo UPDATE del
contador de visitas.

Nosotros tenemos dos escenarios:

unvlog.com, con no pocas visitas al día, en el que hacemos el insert
en una base de datos distinta y en tiempo real.

lacoctelera.com en donde hacemos un insert en una base de datos
distinta por una huella de Javascript y cada x minutos recalculamos en
batch y con prioridad muy baja.

2008/7/15 Xavier N. [email protected]:

2008/7/15 Fernando B. [email protected]:

Pero Xavier, ahí tendrías que lanzar un hilo en paralelo que estuviera
realizando SELECT sobre la misma fila para replicar el escenario
exacto.

Se lanzan 30 procesos en paralelo que hacen 100 SELECTS y 100 UPDATES
a la vez sobre el mismo registro.

Vaya por Dios!! La que he montado aquí, y resulta que no era nada de
todo esto!!

¿Que pasaba?

Pues que se borró el mongrel.pid

Luego Mongrel no se reinició…

Y cogía la vista nueva pero con el controller antiguo, donde no se
cargaba el campo veces_visto!! Ni colisión, ni nada…

En fin…

Fernando C. wrote:

Francesc E. wrote:

Puedes mandar el log de la aplicaci�n cuando te da el error 500.

FLIPANTE EL LOG!!!

Fijaos en esto:

Processing RestaurantesController#lista
Parameters: {“action”=>“lista”, “controller”=>“restaurantes”,
“orden”=>“visitas”}

El código es este:

pagina = (params[:page] ||= 1).to_i
orden = (params[:orden]) || "comentarios"
orden = orden.sub('visitas', 'veces_visto')
orden += " Desc" if 

[“puntuacion_media”,“contenidos_count”,“guardado_count”,
“veces_visto”].include?(orden)
join = @ver_provincia ? “inner join provincias p on p.id =
productos.provincia_id” : “”

@nproductos = Producto.count(:all, :joins => join )
@productos = Producto.paginate(:all, :per_page => 

Producto::PRODUCTOS_EN_LISTA, :page => pagina,
:select => “productos.nombre, productos.localidad,
#{@ver_provincia ? “p.nombre as provincia_,” : “”}
productos.puntuacion_media,
productos.contenidos_count, productos.guardado_count,
productos.id, productos.permalink,
productos.type, productos.anyo,
productos.id_publicado,
productos.veces_visto”,
:joins => join, :order => orden + “, nombre”,
:total_entries => @nproductos)

Normalmente esto funciona bien y genera esta consulta SQL:

SELECT productos.nombre, productos.localidad, p.nombre as provincia_,
productos.puntuacion_media, productos.contenidos_count,
productos.guardado_count, productos.id,
productos.permalink, productos.type, productos.anyo,
productos.id_publicado,
productos.veces_visto FROM productos inner join provincias p on
p.id = productos.provincia_id ORDER BY visitas, nombre LIMIT 0, 50

Y el error del log es este (lo he comprobado varias veces porque no me
lo creía):

ActiveRecord::StatementInvalid (Mysql::Error: Unknown column ‘visitas’
in ‘order clause’: SELECT productos.nombre, productos.localidad,
p.nombre as provincia_, productos.puntuacion_media,
productos.contenidos_count,
productos.guardado_count, productos.id,
productos.permalink, productos.type, productos.anyo FROM productos
inner join provincias p on p.id = productos.provincia_id WHERE
(productos.subtipo_id = 41 ) ORDER BY visitas, nombre LIMIT 0, 50)

Es decir, que cuando no puede acceder al campo veces_visto porque se
está actualizando, me recorta los dos últimos campos del SELECT
(productos.id_publicado, productos.veces_visto) y no me hace el orden =
orden.sub(‘visitas’, ‘veces_visto’). EN VEZ DE UN ERROR DE BASE DE
DATOS, LO QUE FALLA ES EL RAILS!!

�En que maquina tienes la aplicaci�n funcionando?

La aplicación corre sobre un servidor CentOS, en el que también está la
BBDD MySQL.

s2

2008/7/15 Fernando C. [email protected]:

cargaba el campo veces_visto!! Ni colisión, ni nada…
Jaaajajaja

Fernando (Calatayud): podrias hacer un grep por “lock” en app, lib,
vendor/plugins?