Cuando recibimos el pico de visitas provenientes de barrapunto.com nos ocurrió lo que les ha ocurrido a muchos usuarios de Google App Engine: problemas de "over quota". El servicio Google App Engine tiene unas cuotas diarias muy generosas. Sin embargo impone una cuota también a cada petición de forma independiente. Nuestro problema es que estábamos superando el uso de CPU por petición en algunas URLs. ¿Cómo lo resolvimos?
Cachear, cachear, cachear
Utilizamos memcache para no tener que consultar contínuamente el datastore. Bueno, realmente ya utilizábamos memcache días antes, pero ahora tuvimos que hacer un uso aún más intensivo.
Servir las imágenes de usuarios y grupos consumía mucha CPU. También los feeds RSS. Cacheamos todas esas consultas. Nos creamos una sencilla función de utilidad:
def cache(self, key, function, timeout=0):
data = memcache.get(key)
if data is not None:
return data
else:
data = function.__call__()
memcache.add(key, data, timeout)
return dataSi antes obteníamos información con:
resultado = self.obtener_resultado()
Ahora hacemos...
resultado = self.cache('clave', self.obtener_resultado)Cambiar el sistema de plantillas
Cachear los resultados disminuyó de forma importante el consumo de CPU por petición. Sin embargo seguía siendo elevado y los problemas de over quota no cesaban. Nos comentaron que quizá el motor de plantillas django era problemático. Hicimos varias pruebas de carga con jinja2, un motor de plantillas similar al de django, pero no completamente compatible. Las pruebas indicaban que con jinja2 obteníamos una mejora de rendimiento a tener en cuenta. Por lo tanto decidimos migrar a jinja2.
Desnormalizar
Sin embargo los problemas persistían. No sabíamos cómo optimizar aún más. No comprendíamos cómo la página principal podía consumir tanta CPU si sólo estábamos usando cuatro consultas sencillas y perfectamente cacheadas...
Finalmente encontramos el problema. Estábamos cacheando listas de resultados, pero en la plantilla estábamos navegando por las relaciones de estos resultados, lo que provocaba que se hiciera una consulta por resultado. Con 30 resultados a mostrar, aunque estuvieran cacheados, al navegar por sus relaciones estábamos provocando 30 consultas a la base de datos. Por ejemplo esto era lo que hacíamos en la lista de últimos mensajes en los foros:
{{ thread.author.nickname }}Desnormalizamos la base de datos y ahora hacemos lo siguiente:
{{ thread.author_nickname }}Ahora un mensaje de un foro guarda no sólo una referencia al autor, sino también el nickname del mismo. Es decir, añadimos redundancia a los datos, poniendo el nickname del autor en otros objetos. Debido a que un usuario no puede cambiar su nickname y que el autor de un mensaje evidentemente tampoco cambia, fue una desnormalización sencilla.
Después de esta optimización cesaron casi todos los errores. Optimizamos alguna cosa más, pero las anteriores fueron las optimizaciones claves.
No hemos tenido problemas de over quota desde entonces. Estamos muy contentos y hemos aprendido mucho.
Google Developer Day
Hicimos nuestras últimas optimizaciones aprovechando el tiempo que brindaban los talleres del Google Developer Day. Sí, íbamos contra el tiempo, no parábamos :P. Allí hablamos con los ingenieros de Google y les enseñamos la aplicación. Resulta que nuestra web es una de las aplicaciones más grandes que hay actualmente funcionando sobre App Engine. Revisaron nuestra cuenta en el servicio y creemos que fueron generosos dándonos mayor márgen de maniobra en nuestra cuota de servicio. Aunque no lo sabemos a ciencia cierta.
Más optimizaciones
Tenemos algunas optimizaciones más en mente. Por ejemplo, en la URL de las imágenes de usuarios y grupos vamos a incluir un número que indicará la versión de la imagen. Haremos que el navegador cachée todas las imágenes. Que las descargue una vez y nunca más las vuelva a solicitar. Cuando un usuario o un grupo cambie su imágen, el número de versión cambiará, y por tanto la URL también. Para el navegador será una nueva imagen que nunca ha visitado.
debug_mode=ON es el administrador de esta página. Realmente no es una persona, sino que somos todos los que estamos detrás de esta página. Ante cualquier duda o sugerencia procuraremos responder lo mejor y más rápido posible :)
AbeEstrada escribió
hace 1 años
© Copyright 2008-2009 debug_mode=ON | Aviso legal | Contacto | FAQ | ¿Quiénes somos? |
#1
Pues felicidades y se me hace curioso lo de las plantillas de Django, se me hace que no es precisamente que den problema si no mas bien que consumen mas recursos que las de Jinja. Aun asi, felicidades por la excelente aplicacion que han creado.