2011-09-28 24 views
11

En mi clase de fotos tengo esta asociación.¿Rieles cómo tocar el objeto de registro activo sin bloquear?

belongs_to :user, :touch => true 

Un día recibí esta excepción.

A ActiveRecord::StatementInvalid occurred in photos#update: 

Mysql::Error: Deadlock found when trying to get lock; try restarting transaction: 
UPDATE `users` SET `updated_at` = '2011-09-20 14:17:44' WHERE `users`.`id` = 6832 
production/ruby/1.8/gems/activerecord-3.0.10/lib/active_record/connection_adapters/abstract_adapter.rb:207:in `log' 

¿Qué debo hacer para evitar futuras excepciones como esta? Me gustaría que la declaración de actualización que se muestra en el error no use bloqueo si es posible. No creo que usar el bloqueo optimista funcione en este caso porque el bloqueo optimista probablemente generaría un ActiveRecord :: StaleObjectError en su lugar.

Respuesta

8

Ese es un problema que me encontré a mí mismo también.

Respuesta corta: No hay una forma fácil de solucionar este problema. Todos los touch es están envueltos en la misma transacción, de ahí el punto muerto.

Respuesta larga: Supongo que necesita objetos táctiles para invalidar algún tipo de cachés (dependientes). El uso generalmente recomendado de touch solo funciona para una cantidad limitada de "relaciones". P.ej. invalidar el artículo cuando se actualiza el comentario.

Mi solución fue la colección asíncrona (utilizando un trabajo sidekiq) de Objetos de BD que deben ser invalidados. Escribí mi propia lógica de control que define qué (otros) objetos deben invalidarse cuando un objeto cambia. P.ej. comentario ==> artículo.

De esta manera tenemos una forma más detallada de invalidar los objetos dependientes. Además, anulé utilizando un Model.update_all que era mucho más rápido que la "cadena táctil". Solucionó nuestros problemas de interbloqueo (y mayor nivel de detalle y rendimiento para nuestra invalidación de caché).

Consejo adicional: No utilice updated_at. Es muy discutible si un objeto DB realmente cambió porque otro objeto cambió. La sobrescritura del modelo cache_key le permite definir fácilmente una clave de caché personalizada como "#{id}-#{valid_from}". valid_from podría ser una marca de tiempo que defina en sus modelos (y que use en lugar de updated_at).

+0

Gracias por la respuesta. Han pasado 2 años y no estoy del todo seguro de por qué estaba usando el tacto. Invalidar el caché parece una buena razón. Ahora uso barredoras para caducar las entradas de caché. http://guides.rubyonrails.org/caching_with_rails.html#sweepers. Ya no uso el tacto en ningún lado. –

+0

Me gusta la idea de valid_from, pero creo que también necesita agregar el nombre de clase en cache_key, por ejemplo [class]/[id] - [timestamp] según http://signalvnoise.com/posts/3113- how-key-based-cache-expiration-works – iheggie

Cuestiones relacionadas