Implementar la cache con Flask Caching

- Andrés Cruz

EN In english

Implementar la cache con Flask Caching

Uno de los grandes retos que tenemos una vez creada la aplicación es optimizar la misma, para que consuma menos recursos y también para que devuelva las respuestas más rápidamente; estos aspectos son requeridos bajo muchas circunstancias, por ejemplo, para el SEO.

Una vez generado los datos de prueba en Flask, ahora vamos a ver como podemos generar una cache de esos datos.

La velocidad de carga de la página es uno de los factores más importantes que determinará el éxito o el fracaso de una aplicación web.

Flask Caching resuelve permite almacenar datos en caché, para que de esta forma sean devueltos más rápidamente; estos datos son usualmente datos de la base de datos o documentos procesados en HTML, de esta manera, al recibir una petición del usuario, se verifica si está en caché:

  1. Si los resultados están en caché, se devuelven los resultados en caché.
  2. Si los resultados no están en caché, se generan según lo implementado en el controlador/vista y luego se guardan en caché.

Instalación y Configuración Inicial

Vamos a instalar Flask Caching con:

$ pip install Flask-Caching

El siguiente paso, es registrarlo a nivel de la aplicación:

app\my_app\__init__.py

from flask_caching import Cache 
***
#cache
cache = Cache()
cache.init_app(app)
# blueprints
***

El siguiente paso es configurar el almacenamiento de la caché, que pueden ser los que puedes consultar en el siguiente enlace:

https://flask-caching.readthedocs.io/en/latest/#built-in-cache-backends

Usaremos:

my_app\config.py

class DevConfig(Config): 
    ***
    CACHE_TYPE = 'SimpleCache'

En cuanto al tipo de caché (CACHE_TYPE), existen varias opciones según el entorno:

  • Simple: Es la opción por defecto y utiliza la memoria del sistema.
  • Redis: Ideal para entornos de producción con alto tráfico (requiere tener Redis instalado).

La opción SimpleCache le dice a Flask Caché que almacena los resultados en la memoria en un diccionario de Python, lo cual, para la gran mayoría de las aplicaciones de Flask será suficiente.

Implementación de Caché en Rutas: Almacenar templates y funciones

Para almacenar en caché el resultado de un controlador/vista, basta con indicar el decorador de cache.cached:

my_app\tasks\controllers.py

from my_app import cache
***
@taskRoute.route('/')
@cache.cached(timeout=60)
def index():
   return render_template('dashboard/task/index.html', tasks=operations.pagination(request.args.get('page', 1, type=int), request.args.get('size', 10, type=int)))

El parámetro de timeout especifica cuántos segundos debe durar la caché del resultado.

Cuando se emplea la caché en una vista, se debe de colocar el decorador de la ruta antes del decorador de caché.

Simulando Cargas Pesadas

Como trabajamos en localhost, las respuestas suelen ser instantáneas. Para apreciar el beneficio de la caché, vamos a simular una tarea pesada (como una conexión lenta a una base de datos o procesamiento de imágenes) usando la función sleep de Python:

import time
from flask import Blueprint
@products.route('/test2')
@cache.cached(timeout=60) # Aplicamos el decorador de caché
def test2():
   time.sleep(3) # Pausamos la aplicación por 3 segundos
   return "Hola Mundo"

Funciones de almacenamiento en caché sin parámetros

Los resultados de los controladores no son lo único que se puede almacenar en caché. Para almacenar en caché cualquier función de Python, simplemente agregue un decorador similar a la definición de la función, de la siguiente manera:

@cache.cached(timeout=60, key_prefix='unique_key') 
def myFunction():
   ***

El argumento key_prefix es necesario para que Flask Caching pueda almacenar los resultados de las funciones que no son de vista. El valor debe ser único para cada función que queramos almacenar en caché.

Funciones de almacenamiento en caché con parámetros

El decorador anterior no puede ser empleado en funciones que tengan argumentos, en estos casos debemos de usar el decorador de memoize para que se incluyan en la caché:

class Person(db.Model):
    @cache.memoize(50)
    def has_membership(self, role_id):
        return Group.query.filter_by(user=self, role_id=role_id).count() >= 1

Memoize también está diseñado para métodos, ya que tendrá en cuenta la identidad del argumento 'self' o 'cls' como parte de la clave de la caché.

Mientras desarrollamos la aplicación, no queremos que las funciones de caché están funcionando ya que las pantallas cambian a medida que actualizamos la misma para evitar esto, establecemos la variable CACHE_TYPE en nulo para la configuración en desarrollo:

my_app\config.py
class Config(object): 
    ***
    CACHE_TYPE = 'SimpleCache'
 
class DevConfig(Config): 
    ***
    CACHE_TYPE = 'null'

¿Cuándo usar Caché?

Es importante tener criterio al aplicar estas herramientas. En módulos administrativos (donde se listan productos para edición), la caché no suele ser muy útil, ya que el administrador necesita ver los cambios en tiempo real.

Sin embargo, en la vista del cliente final (como un blog o un catálogo público), la caché es fundamental:

  • Volumen de usuarios: Si 1,000 usuarios consultan el mismo post, no tiene sentido procesar la misma consulta mil veces.
  • SEO: Los buscadores como Google valoran positivamente que el tiempo de respuesta sea mínimo.

Caché de Funciones y Memorización

Si necesitas guardar el resultado de una función que no es una ruta (por ejemplo, un cálculo complejo en un modelo), puedes usar el mismo decorador.

  • @cache.cached: Para funciones sin argumentos.
  • @cache.memoize: Es la función ideal cuando la respuesta depende de ciertos argumentos (como un ID de usuario). Así, el sistema guardará una caché distinta para cada combinación de parámetros.
@cache.memoize(timeout=50)
def consulta_pesada(parametro):
   # Lógica de base de datos...
   return resultado

Siguiente paso, implementa un sistema de roles y permisos en Flask.

Veremos como emplear el sistema de cache conocido como Flask Caching, con el cual podremos guardar en cache los controladores/views o funciones con o sin argumentos.

Acepto recibir anuncios de interes sobre este Blog.

Andrés Cruz

EN In english