2009-04-09 8 views
6

Tengo una aplicación Rails bastante grande, que usa memcached en un servidor separado como su almacén de memoria caché.¿Rieles con Memcache devolviendo el objeto en caché incorrecto?

El problema es que obtengo al azar errores en el entorno de producción que parecen para indicar que memcached está devolviendo un objeto incorrecto.

Ejemplos:

En este ejemplo, current_site es un método de ayuda que accede a un método en el modelo Site que utiliza Rails.cache para almacenar en caché el modelo

ActionView::TemplateError in ListingsController#edit 
undefined method `settings' for #<String:0xb565f8a0> 

On line #12 of app/views/layouts/site.html.erb 

    9:   <meta name="robots" content="noodp, all" /> 
    10:   <meta name="distribution" content="Global" /> 
    11: 
    12:   <% unless current_site.settings[:google_webmaster_verification_code].blank? %> 
    13:   <meta name="verify-v1" content="<%= current_site.settings[:google_webmaster_verification_code] %>" /> 
    14:   <% end %> 
    15: 

en contraste con ....

ActionView::TemplateError in ApplicationController#not_found 
undefined method `settings' for #<Category:0xd5c6c34> 

On line #12 of app/views/layouts/site.html.erb 

    9:   <meta name="robots" content="noodp, all" /> 
    10:   <meta name="distribution" content="Global" /> 
    11: 
    12:   <% unless current_site.settings[:google_webmaster_verification_code].blank? %> 
    13:   <meta name="verify-v1" content="<%= current_site.settings[:google_webmaster_verification_code] %>" /> 
    14:   <% end %> 
    15: 

Cuando ambos se deben de devolver un modelo Site!

Otro ejemplo de caché comporta de forma extraña:

ActionView::TemplateError in AccountsController#show 
can't convert Category into String 

On line #141 of app/views/layouts/site.html.erb 

    138:     <li<%= class="first" if i == 0 %>><%= link_to top_level_category.title, top_level_category.path %></li><% end %> 
    139:    </ul> 
    140:   <% end %> 
    141:   <% cache bottom_pages do %> 
    142:    <ul><% Page.top_level.active.show_in_navigation.find(:all, :include => :slugs).each_with_index do |top_level_page, i| %> 
    143:     <li<%= class="first" if i == 0 %>><%= link_to top_level_page.title, top_level_page.path %></li><% end %> 
    144:    </ul> 

Alguien ha encontrado algo como esto antes? ¿Alguien tiene ideas para diagnosticar este problema irreplicable? Intenté desconectar gemas de memcached client, pensando que tal vez era un error extraño, ¡pero esto no tuvo ningún efecto! Gracias.

+0

¿Esto sucede en el entorno de producción o en el modo de desarrollo? – pauliephonic

Respuesta

10

Esto fue causado por Passenger que comparte su conexión con el servidor de Memcached. Compruebe http://www.modrails.com/documentation/Users%20guide.html#_example_1_memcached_connection_sharing_harmful.

La solución era simplemente para cambiar los carriles engendro del pasajero a conservative.

+0

¡GRACIAS! Esto me ha estado volviendo loco durante los últimos días. –

+0

No puedo expresar lo increíble que fue encontrar esta respuesta. ¡Esto nos estaba volviendo locos! –

1

Algunas cosas que pueden ayudar:

  • Añadir instrumentación/registro a current_site para ver exactamente lo que se está devolviendo.
  • claves ¿Cómo estás especificando en Memcache? Podría accidentalmente estar usando la misma llave en dos lugares diferentes para dos objetos diferentes.
  • Uso memcached-tool host:port dump > /tmp/keys a mirar lo que está realmente en su Memcache.
  • Su memcached está detrás de un cortafuegos y no expuesto en una IP pública, ¿verdad?
+0

1) Hecho, verifica que esto es realmente lo que está sucediendo. 2) Comprobado de nuevo, no verdadero 3) ¿Hizo esto, pero no estoy seguro de cómo es útil? 4) Corregir Gracias! – Ben

+0

Espere hasta que vea el comportamiento impar, y luego mire lo que 'memcached-tool host: port dump' escupe, para ver si incluso * tiene * los datos' fantasma '... si no, el problema está en algún lugar afuera de memcahce. Más allá de eso, no tengo ni idea ... –

0

Sí, he tenido que esto ocurra. Conmigo, fue porque estaba haciendo Rails.cache.fetch(key) y la clave estaba en blanco.

He hecho algunas jugar en la consola de Rails y con lo siguiente:

Rails.cache.read validkey # Get back the proper data 
Rails.cache.fetch('') { 'abc' } # Error in rails log: 'MemCacheError():' 
Rails.cache.read validkey # Get back nil 
Rails.cache.read validkey # May get back proper data 
1

Tenía este problema también y se resolvió añadiendo require_dependency para cada clase/modelo antes de la operación unmarshal por el proveedor de memoria caché . Tal vez en el entorno de producción esto no sea necesario porque la opción: config.cache_class está configurada como verdadera, pero en prueba y desarrollo es falsa.

De hecho, la Memcache (es el proveedor de memoria caché que estoy usando) no encuentran la clase referenciada para hacer el unmarshal y luego subir este error.

En este post tiene un poco mejor solución a este problema: http://kballcodes.com/2009/09/05/rails-memcached-a-better-solution-to-the-undefined-classmodule-problem/

Saludos!

0

agregando un comentario a esto en caso de que alguien más se presente ... la url kballcodes.com ya no es válida (aunque todavía puede acceder a ella a través del archive.org). En los comentarios en esa publicación de blog, alguien describió una manera de hacer que Marshal intente cargar el objeto en cuestión si primero arroja el error de "clase/módulo no definido". He incluido ese código a continuación y he hecho referencia al autor original debajo del ejemplo del código.

Añadir esto a un fichero de inicialización en la carpeta de RAILS_ROOT/config/inicializadores /:

# 
# Marshal.load is a C-method built into Ruby; because it's so low-level, it 
# bypasses the full classloading chain in Ruby, in particular the #const_missing 
# hook that Rails uses to auto-load classes as they're referenced. This monkey 
# patch catches the generated exceptions, parses the message to determine the 
# offending constant name, loads the constant, and tries again. 
# 
# This solution is adapted from here: 
# http://kballcodes.com/2009/09/05/rails-memcached-a-better-solution-to-the-undefined-classmodule-problem/ 
# 
class <<Marshal 
    def load_with_rails_classloader(*args) 
    begin 
     load_without_rails_classloader(*args) 
    rescue ArgumentError, NameError => e 
     if e.message =~ %r(undefined class/module) 
     const = e.message.split(' ').last 
     const.constantize 
     retry 
     else 
     raise(e) 
     end 
    end 
    end 

    alias_method_chain :load, :rails_classloader 
end 

Todo el crédito para este va a Matt Brown que hizo this code as a pastie y comentado en artículo ahora muerto superior:

Cuestiones relacionadas