¿Cómo podría uno hacer que los objetos de Python sean persistentes en una aplicación web?

Estoy escribiendo una aplicación web razonablemente compleja. El backend de Python ejecuta un algoritmo cuyo estado depende de los datos almacenados en varias tablas de bases de datos interrelacionadas que no cambian a menudo, más datos específicos del usuario que cambian a menudo. El estado por usuario del algoritmo experimenta muchos cambios pequeños cuando un usuario trabaja con la aplicación. Este algoritmo se usa a menudo durante el trabajo de cada usuario para tomar ciertas decisiones importantes.

Por motivos de rendimiento, la reinicialización del estado en cada solicitud desde los datos de la base de datos (semi-normalizados) rápidamente se vuelve inviable. Sería altamente preferible, por ejemplo, almacenar en caché el objeto Python del estado de alguna manera para que pueda ser simplemente usado y / o actualizado cuando sea necesario. Sin embargo, dado que se trata de una aplicación web, hay varios procesos que atienden solicitudes, por lo que no es posible utilizar una variable global.

He intentado serializar el objeto relevante (a través de pickle) y guardar los datos serializados en la base de datos, y ahora estoy experimentando con el almacenamiento en caché de los datos serializados a través de memcached. Sin embargo, esto todavía tiene la sobrecarga significativa de serializar y deserializar el objeto a menudo.

He buscado soluciones de memoria compartida, pero lo único relevante que he encontrado es POSH . Sin embargo, parece que POSH no se usa mucho y no me parece fácil integrar dicho componente experimental en mi aplicación.

¡Necesito un consejo! Esta es mi primera oportunidad de desarrollar una aplicación web, así que espero que este sea un problema lo suficientemente común como para que existan soluciones bien conocidas para tales problemas. En este punto, las soluciones que asumen que el back-end de Python se está ejecutando en un solo servidor serían suficientes, pero los puntos adicionales para las soluciones que se escalan a múltiples servidores también 🙂

    Notas:

    • Tengo esta aplicación funcionando, actualmente en vivo y con usuarios activos. Comencé sin hacer ninguna optimización prematura, y luego optimicé según fuera necesario. He realizado las mediciones y pruebas para asegurarme de que el problema mencionado anteriormente sea el cuello de botella real. Estoy bastante seguro de que podría exprimir más el rendimiento de la configuración actual, pero quería preguntar si hay una mejor manera.
    • La configuración en sí sigue siendo un trabajo en progreso; Supongamos que la architecture del sistema puede ser lo que mejor se adapte a su solución.

    Creo que el marco de multiprocesamiento tiene lo que podría ser aplicable aquí, a saber, el módulo ctypes compartido.

    El multiprocesamiento es bastante nuevo para Python, por lo que puede tener algunas rarezas. No estoy seguro de si la solución funciona con procesos no generados mediante multiprocessing .

    Tenga cuidado con la optimización prematura.

    Adición: El “backend de Python ejecuta un algoritmo cuyo estado …” es la sesión en el marco web. Eso es. Deje que el framework Django mantenga el estado de la sesión en caché. Período.

    “El estado por usuario del algoritmo sufre muchos pequeños cambios cuando un usuario trabaja con la aplicación”. La mayoría de los marcos web ofrecen un objeto de sesión en caché. Muchas veces es de muy alto rendimiento. Vea la documentación de la sesión de Django para esto.

    Consejo. [Revisado]

    Parece que tienes algo que funciona. Aproveche para aprender su marco, aprenda las herramientas y aprenda qué perillas puede girar sin sudar. Específicamente, utilizando el estado de sesión.

    En segundo lugar, juegue con el caché, la administración de sesiones y las cosas que son fáciles de ajustar, y vea si tiene la velocidad suficiente. Averigüe si el socket MySQL o la tubería con nombre es más rápido probándolos. Estas son las optimizaciones sin progtwigción.

    Tercero, mida el rendimiento para encontrar su cuello de botella real. Esté preparado para proporcionar (y defender) las mediciones como lo suficientemente detalladas para que sean útiles y lo suficientemente estables para proporcionar una comparación significativa de alternativas.

    Por ejemplo, muestre la diferencia de rendimiento entre sesiones persistentes y sesiones en caché.

    Creo que puedes darle una oportunidad a ZODB.

    “Una característica importante de ZODB es la transparencia. No necesita escribir ningún código para leer o escribir explícitamente sus objetos en una base de datos. Simplemente coloque sus objetos persistentes en un contenedor que funciona como un diccionario de Python. Todo dentro de este el diccionario se guarda en la base de datos. Se dice que este diccionario es la “raíz” de la base de datos. Es como una bolsa mágica; cualquier objeto de Python que coloques en él se vuelve persistente “.

    Initailly era una parte integral de Zope, pero últimamente también está disponible un paquete independiente.

    Tiene la siguiente limitación:

    “En realidad, existen algunas restricciones sobre lo que puede almacenar en el ZODB. Puede almacenar cualquier objeto que pueda ser” decapado “en un formato de serie multiplataforma estándar. Objetos como listas, diccionarios y números pueden ser decapados. Objetos “Los archivos similares, los sockets y los objetos de código Python no se pueden almacenar en la base de datos porque no se pueden decapar”.

    Lo he leído, pero no lo he probado.

    Otra cosa posible podría ser una base de datos sqlite en memoria, que puede acelerar un poco el proceso – al ser una base de datos en memoria, pero aún así tendría que hacer las cosas de serialización y todo. Nota: En memoria db es caro en recursos.

    Aquí hay un enlace: http://www.zope.org/Documentation/Articles/ZODB1

    En primer lugar, su enfoque no es una práctica de desarrollo web común. Incluso se están utilizando los subprocesos múltiples, las aplicaciones web están diseñadas para poder ejecutar entornos de multiprocesamiento, tanto para escalabilidad como para una implementación más sencilla.

    Si solo necesita inicializar un objeto grande y no necesita cambiarlo más tarde, puede hacerlo fácilmente utilizando una variable global que se inicializa mientras se crea su aplicación WSGI, o el módulo contiene el objeto que se está cargando, etc. multiprocesamiento hará bien para usted.

    Si necesita cambiar el objeto y acceder a él desde cada subproceso, debe asegurarse de que su objeto sea seguro para el subproceso, use lockings para asegurarse de ello. Y usa un solo contexto de servidor, un proceso. Cualquier servidor Python de subprocesos múltiples le servirá bien, también FCGI es una buena opción para este tipo de diseño.

    Pero, si varios subprocesos acceden y cambian su objeto, los lockings pueden tener un efecto realmente negativo en su ganancia de rendimiento, lo que probablemente hará que todos los beneficios desaparezcan.

    Esto es Durus, un sistema de objetos persistentes para aplicaciones escritas en el lenguaje de progtwigción Python. Durus ofrece una manera fácil de usar y mantener una colección consistente de instancias de objetos utilizadas por uno o más procesos. El acceso y el cambio de una instancia persistente se administra a través de una instancia de Connection en caché que incluye los métodos commit () y abort () para que los cambios sean transaccionales.

    http://www.mems-exchange.org/software/durus/

    Lo he usado antes en algunos códigos de investigación, donde quería conservar los resultados de ciertos cálculos. Eventualmente cambié a pytables ya que satisfacía mis necesidades mejor.

    Otra opción es revisar el requisito de estado, parece que si la serialización es el cuello de la botella, el objeto es muy grande. ¿Realmente necesitas un objeto tan grande?

    Sé que en el podcast de Stackoverflow 27, los chicos de reddit discuten lo que usan para el estado, por lo que tal vez sea útil escucharlo.