<p>gunicorn 19.x causa problemas con django-1.7.1 y gevent</p>

Creado en 4 nov. 2014  ·  53Comentarios  ·  Fuente: benoitc/gunicorn

Hola,

Veo este error con gunicorn 19.x (probado con 19.0 y 19.1.1 ) usando el trabajador gevent . Estoy usando django-1.7.1 .

esta es la excepción:

Traceback:
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/core/handlers/base.py" in get_response
  87.                 response = middleware_method(request)
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/auth/middleware.py" in process_request
  34.         if user and hasattr(user, 'get_session_auth_hash'):
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/utils/functional.py" in inner
  224.             self._setup()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/utils/functional.py" in _setup
  357.         self._wrapped = self._setupfunc()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/auth/middleware.py" in <lambda>
  23.         request.user = SimpleLazyObject(lambda: get_user(request))
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/auth/middleware.py" in get_user
  11.         request._cached_user = auth.get_user(request)
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/auth/__init__.py" in get_user
  151.         user_id = request.session[SESSION_KEY]
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/sessions/backends/base.py" in __getitem__
  49.         return self._session[key]
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/sessions/backends/base.py" in _get_session
  175.                 self._session_cache = self.load()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/contrib/sessions/backends/db.py" in load
  21.                 expire_date__gt=timezone.now()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/manager.py" in manager_method
  92.                 return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py" in get
  351.         num = len(clone)
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py" in __len__
  122.         self._fetch_all()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py" in _fetch_all
  966.             self._result_cache = list(self.iterator())
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/query.py" in iterator
  265.         for row in compiler.results_iter():
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in results_iter
  700.         for rows in self.execute_sql(MULTI):
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/models/sql/compiler.py" in execute_sql
  784.         cursor = self.connection.cursor()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/backends/__init__.py" in cursor
  163.         self.validate_thread_sharing()
File "/home/sieve/sitroom.sieve.com.br/shared/env/local/lib/python2.7/site-packages/django/db/backends/__init__.py" in validate_thread_sharing
  515.                 % (self.alias, self._thread_ident, thread.get_ident()))

Exception Type: DatabaseError at /
Exception Value: DatabaseWrapper objects created in a thread can only be used in that same thread. The object with alias 'default' was created in thread id 140049505195808 and this is thread id 61130224.

Aquí está mi gunicorn.conf.py :

##
# Gunicorn config at 
# Managed by Chef - Local Changes will be Nuked from Orbit (just to be sure)
##

# What ports/sockets to listen on, and what options for them.
bind = "127.0.0.1:9300"

# The maximum number of pending connections
backlog = 2048

# What the timeout for killing busy workers is, in seconds
timeout = 300

# How long to wait for requests on a Keep-Alive connection, in seconds
keepalive = 2

# The maxium number of requests a worker will process before restarting
max_requests = 1024

# Whether the app should be pre-loaded
preload_app = True

# How many worker processes
workers = 16

# Type of worker to use
worker_class = "gevent"

# The granularity of error log outputs.
loglevel = "error"

El error ocurre cada vez, solo acceder a cualquier URL de la aplicación django (que depende de cualquier modelo, por supuesto) desencadena este error. ¿Algunas ideas?
Dado que cambiar a gunicorn-18.0 resuelve el problema, supongo que gunicorn-19.x está haciendo algo diferente.

Si necesita información adicional, hágamelo saber y la publicaré aquí.

Por ahora estoy usando 18.0 .

Muchas gracias.

( FeaturWorker FeaturCore ThirdpartGevent Deploy Investigation - Bugs -

Comentario más útil

La solución propuesta de

Todos 53 comentarios

Primero, solo una pregunta: ¿ayuda sacar la opción de precarga?
Creo recordar que se informó de otro problema similar.
Si cambiar esa opción ayuda, entonces debemos haber cambiado algo sobre cómo se cargan las aplicaciones de Django y será más fácil para nosotros rastrearlo.

Creo que importamos la aplicación django wsgi en el proceso maestro, que al menos en django 1.7, inicializa todos los modelos de django en la importación.

Hola @tilgovi , gracias por tu tiempo. No puedo confirmar que usando preload_app = False pueda usar gunicorn-19.1.1 y django-1.7.1 .

¿Tienes alguna idea de lo que hace la nube de Gunicorn de manera diferente al respecto en las versiones 19.x ?

Ahora tengo un segundo problema. Yo uso Raven [1] en mi aplicación django. Y de acuerdo con los documentos de Raven, cuando se usa un middleware WSGU [1] es posible usar Raven como un objeto WSGI (envolviendo la aplicación original django WSGI), y cuando se usa gunicorn (como un comando externo, no el run_gunicorn django command) debemos agregar un gancho a gunicorn [2]. Cuando este gancho está habilitado, gunicorn solo arranca si preload_app = True , pero luego gevent no funciona. Cuando uso preload_app = True con el gancho habilitado, gunicorn ni siquiera arranca, dándome a mí y ImportError diciendo que no puede importar mi "DJANGO_SETTINGS_MODULE".

Cuervo es 4.2.3

Por ahora volveré a gunicorn 18.x , ya que funciona con ambas configuraciones (gevent y raven como middleware WSGI)
¿Debo abrir otro problema sobre esto?

[1] http://raven.readthedocs.org/en/latest/config/django.html#wsgi -middleware
[2] http://raven.readthedocs.org/en/latest/config/django.html#gunicorn

@daltonmatos, la forma en que se configuran las bases de datos en django 1.7.1 probablemente ha cambiado, aparentemente no es segura para subprocesos. No estoy seguro de qué hacer en ese momento.

Una cosa más, me acabo de enterar hoy,
el manejo de sesiones en django 1.7 con gunicorn 19.3.0 expira en segundos.

Tuve que volver a cambiar a 18 para que funcionara.

FYI.

Este parece ser el mismo problema que https://github.com/benoitc/gunicorn/issues/879 , que se cerró pero no se solucionó. Hace gunicorn 19.x. inutilizable con Django. ¿Quizás solo para el trabajador gentil? No he probado otros tipos de trabajadores.

@tilgovi me pregunto si no se debe al cambio hecho en el entorno. en 19.x establecemos wsgi.multithread en true donde estaba false en 18.x:

https://github.com/benoitc/gunicorn/blob/master/gunicorn/workers/ggevent.py#L99

Este cambio puede manejarse de manera diferente en django. Y un greenlet no es realmente un hilo. ¿Pensamientos? Al menos, tal vez podríamos intentar revertirlo y verificar si resuelve el problema. ¿Tenemos una prueba o una forma reproducible para este problema?

Oh, apuesto a que ese es el problema. Buena memoria, @benoitc.

Todavía no sé por qué. Como dije en ese momento para justificar el cambio, no puedo imaginar que se rompa algo porque significaría que el marco solo tomaría más precauciones sobre el intercambio de datos. Evidentemente, puedo estar equivocado.

Empujé la rama fix/927 para verificar si la solución anterior funciona. ¡Por favor prueba!

Todavía puedo reproducir este error usando fix / 927 con django 1.8.1 (y 1.7.1) y gevent 1.0.1

Eso me tranquiliza, incluso si todavía no conocemos la causa.

Así que intenté usar la aplicación de ejemplo y no reproduje el problema ejecutando la siguiente línea de comando:

gunicorn --env DJANGO_SETTINGS_MODULE=testing.settings -k gevent -w3 wsgi:application

¿Alguien me puede dar un ejemplo reproducible?

Todavía puedo reproducir este problema invocando gunicorn así:

    gunicorn --env DJANGO_SETTINGS_MODULE=settings -w 3 --max-requests=1000 -b 0.0.0.0:8000 -k gevent --config ogs/gunicorn.py  --pythonpath ogs wsgi:application

¿Hay algún detalle relevante que necesite?

@brockhaywood debería ser project.settings , pero sí, necesito tener una forma de reproducirlo, ¿tienes algún código mínimo para compartir?

Mirando el rastro de arriba, también podría ser el parche del mono. Quizás los hilos no deberían ser parcheados. ¿Puedes probar el siguiente parche?

--- a/gunicorn/workers/ggevent.py
+++ b/gunicorn/workers/ggevent.py
@@ -60,9 +60,9 @@ class GeventWorker(AsyncWorker):

         # if the new version is used make sure to patch subprocess
         if gevent.version_info[0] == 0:
-            monkey.patch_all()
+            monkey.patch_all(thread=False)
         else:
-            monkey.patch_all(subprocess=True)
+            monkey.patch_all(subprocess=True, thread=False)

         # monkey patch sendfile to make it none blocking
         patch_sendfile()

El uso del parche anterior con el siguiente wsgi ya no produce el error especificado:

import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "settings")

from gevent import monkey
monkey.patch_all(thread=False)

from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()

from django.core.cache.backends.memcached import BaseMemcachedCache
BaseMemcachedCache.close = lambda self, **kwargs: None

Ahora veo un error nuevo y diferente, pero no tengo idea de si está relacionado:

ProgrammingError: close cannot be used while an asynchronous query is underway

Esto está ocurriendo, 1 de cada 10/15 solicitudes está haciendo un recuento en un modelo. Sospecho que se trata de un error en mi aplicación que ahora está descubierto.

@brockhaywood genial! por cierto, ¿cuál es tu versión de gevent? Para su último error, muere no parece estar relacionado con gunicorn.

¡Sí, buenas noticias! En la versión 1.0.1 de gevent.

@tilgovi, así que me pregunto si no deberíamos parchear gunicorn quitando el parche de mono de hilo. ¿Quizás también deberíamos establecer wsgi.multithread en False ? ¿Pensamientos?

@benoitc Puedo reproducir el nuevo error al que hice referencia anteriormente con una aplicación Django muy simple que tiene solo una vista y hace una consulta de modelo simple y cuenta si ejecuto múltiples solicitudes concurrentes.

¿Tiene alguna sugerencia sobre qué componente es el culpable más probable? ¿Es probable que se trate de un problema grave o psico-verde?

Más probablemente psycogreen. ¿Estás usando ese gancho para configurarlo?

def def_post_fork(server, worker):
    from psycogreen.gevent import psyco_gevent
    psyco_gevent.make_psycopg_green()
    worker.log.info("Made Psycopg Green")

Oh, de hecho estoy usando

def post_fork(server, worker):    
    from psycogreen.gevent import patch_psycopg
    patch_psycopg()

Probaré tu sugerencia.

Ok, intentaré abrir un ticket con psycogreen

¿Puedes intentar usar el gancho post_worker_init lugar de post_fork ?

Todavía me ocurre cuando parcheo psycogreen en post_worker_init.

También envié un ticket a psycogreen:
https://bitbucket.org/dvarrazzo/psycogreen/issue/2/databaseerror-used-with-asynchronous-query

@brockhaywood OK: / También encontré este enlace: https://bitbucket.org/dvarrazzo/psycogreen-hg/issue/1/databaseerror-execute-used-with . Sin embargo, no estoy seguro de si podría ayudarlo.

Eché un vistazo a eso y parece que es el mismo problema, pero no vi una solución, desafortunadamente.

@brockhaywood, ¿ puede compartir algún ejemplo simple de código que pueda ayudarme a reproducir el problema? Lo echaré un vistazo mañana.

Aquí el proyecto simple que estoy usando para reproducir:
https://github.com/brockhaywood/gunicorn_gevent_issue

¡Gracias!

El domingo, 17 de mayo de 2015 a las 10:34 p.m. brockhaywood [email protected]
escribió:

Aquí el proyecto simple que estoy usando para reproducir:
https://github.com/brockhaywood/gunicorn_gevent_issue

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/benoitc/gunicorn/issues/927#issuecomment -102852497.

@benoitc ¿ alguna idea sobre esto?

@brockhaywood lo siento, no tuve tiempo de probarlo. Como mañana estoy encerrado en un vuelo, lo haré de todos modos :)

Gracias
El 26 de mayo de 2015 a las 13:17, "Benoit Chesneau" [email protected] escribió:

@brockhaywood https://github.com/brockhaywood lo siento, no tuve tiempo
para probarlo. Como mañana estoy encerrado en un vuelo, lo haré de todos modos :)

-
Responda a este correo electrónico directamente o véalo en GitHub
https://github.com/benoitc/gunicorn/issues/927#issuecomment -105652947.

Estaba experimentando el mismo problema de DatabaseWrapper cuando vi este hilo.

Puedo confirmar que eliminar la opción preload_app de mi archivo de configuración gunicorn soluciona el problema. Estoy usando:

  • Django == 1.7.7
  • gevent == 1.0.2
  • gunicorn == 19.3.0
  • psycopg2 == 2.5.2
  • psycogreen == 1.0
  • gunicorn envuelto de PgBouncer

(saltando ya que hemos estado viendo los mismos problemas que se describen en este hilo)

Parece que estos problemas están siendo causados ​​por la solución del problema # 478 (no aplique parches al maestro).

Al no parchear el maestro, la precarga de la aplicación crea fundamentalmente una falta de coincidencia si está utilizando trabajadores gevent. Cualquier código de aplicación que se ejecute como parte de la precarga se ejecuta en un entorno que no tiene parches de mono. Cualquier código de aplicación que se ejecute en el trabajador se ejecuta en un entorno parcheado de mono.

Para nosotros, simplemente restaurar el setup método para el gevent clase trabajadora como se le define en 18.x, por lo que el maestro es mono parcheado y es mono parcheado antes de la precarga aplicación, fija todo nuestros problemas. Esto incluye el error DatabaseWrapper , así como otro problema que estábamos viendo en el que los trabajadores de gevent se colgaban y se agotaban. No fueron necesarios otros cambios (no tenemos que deshabilitar el parche de mono threads , por ejemplo).

Todavía dudaría mucho en parchear el proceso maestro, pero si quieres hacerlo, es posible que puedas usar los ganchos del servidor en lugar de parchear el propio gunicorn. Si puede hacer eso, agradecería cualquier forma en que podamos documentar eso en algún lugar que sea mejor que aquí.

Creo que el plan a corto plazo para nosotros es crear una bifurcación gunicorn (interna) con este parche aplicado.

Investigaré mover un paso de parche de mono en nuestro código wsgi, pero esto todavía parece algo que debería hacerse dentro del código gunicorn (incluso como una opción configurable). La aplicación de parches en el código de la aplicación precargada afectará al maestro de una forma u otra, y el objetivo sería mantener el estado parcheado del mono de los trabajadores sincronizado con el maestro. La aplicación del parche del mono maestro en el código de la aplicación significa que los desarrolladores de aplicaciones que dependen de este comportamiento siempre se pondrán al día con los cambios que haga gunicorn sobre cuándo / cómo parchea al trabajador.

Si funciona parchear temprano, tal vez debería considerarse como un cambio.

No puedo recordar si ese fue alguna vez el caso y si eso causó problemas, pero siempre es complicado con parches de monos.

Nuestra propia aplicación gunicorn de producción requiere precarga y, por lo tanto, también requiere un parche de mono en el maestro (usamos el script pserve Pyramid / paste para iniciar la aplicación y cargar gunicorn desde un archivo ini; tenemos una envoltura alrededor para ser asegúrese de que el parche de mono ocurra lo antes posible).

Soy un mantenedor actual de gevent. Me complace analizar cualquier problema en gevent necesario para que este escenario funcione mejor. Esperamos eliminar 1.1 pronto y sería genial si esta fuera una opción compatible.

Intenté aplicar la misma lógica de parche de mono en la parte superior de nuestra implementación de wsgi, y comenzamos a ver el problema DatabaseWrapper . Sin embargo, no estoy del todo seguro de por qué: mirando la invocación de wsgi en GeventWorker.setup (como un efecto secundario de la asignación de worker_class . Si estoy leyendo el código correcto.

Haré un seguimiento si tengo un eureka o tengo suerte con el código de la aplicación, pero hasta ahora para nosotros, el parcheo anterior en el código maestro de gunicorn es la única solución que resuelve todos nuestros problemas.

@jamadden muchas gracias.

@jzupko, por lo general, es importante que el parche se post_fork en un archivo gunicorn.conf.py . Sin embargo, me acabo de dar cuenta de que la precarga de la aplicación ocurre antes que cualquiera de los enlaces del servidor y no está claro dónde se haría.

@benoitc, ¿tenemos algún mecanismo para la configuración específica del trabajador?

Estoy agregando esto al hito de la versión 20 y sugiriendo que consideremos a monkey parchear el maestro, tal vez, si se puede hacer sin volver a introducir problemas como el # 478.

@tilgovi tienes el método setup del trabajador que creo que se puede usar para eso.

¿Alguien tiene una solución temporal para esto hasta que salga la versión 20.0? ¿O es la única solución para elegir entre deshabilitar preload_app o bajar de categoría a 18.0?

@fletom arreglo temporal para? ¿Probaste el último maestro? ¿Funcionó para ti?

@benoitc Me refiero a una solución para el error "Objetos DatabaseWrapper".

En realidad, el maestro gunicornio actual no funciona en absoluto para mí. En 19.6.0 regular obtengo esto (normal):

[2017-01-05 15:46:32 -0500] [3222] [INFO] Starting gunicorn 19.6.0
[2017-01-05 15:46:32 -0500] [3222] [INFO] Listening at: http://127.0.0.1:8000 (3222)
[2017-01-05 15:46:32 -0500] [3222] [INFO] Using worker: gevent
[2017-01-05 15:46:32 -0500] [3226] [INFO] Booting worker with pid: 3226
[2017-01-05 15:46:32 -0500] [3227] [INFO] Booting worker with pid: 3227
[2017-01-05 15:46:32 -0500] [3228] [INFO] Booting worker with pid: 3228
[2017-01-05 15:46:32 -0500] [3229] [INFO] Booting worker with pid: 3229

Y en el último maestro con la misma configuración 100% exacta obtengo esto, y nada escuchando en ningún puerto por lo que puedo decir:

[2017-01-05 15:47:19 -0500] [3308] [INFO] Starting gunicorn 19.6.0
[2017-01-05 15:47:19 -0500] [3308] [INFO] Listening at:  (3308)
[2017-01-05 15:47:19 -0500] [3308] [INFO] Using worker: gevent
[2017-01-05 15:47:19 -0500] [3312] [INFO] Booting worker with pid: 3312
[2017-01-05 15:47:19 -0500] [3313] [INFO] Booting worker with pid: 3313
[2017-01-05 15:47:19 -0500] [3314] [INFO] Booting worker with pid: 3314
[2017-01-05 15:47:19 -0500] [3315] [INFO] Booting worker with pid: 3315

¿Parece molesto?

@fletom ¿cómo se lanza gunicorn?

@benoitc En este caso, es gunicorn -c gunicorn_conf.py <appname>.wsgi .

Mi gunicorn_conf.py es:

workers = 4

worker_class = 'gevent'

preload_app = False

Estoy tropezando con este problema con gunicorn==18.0 . En gunicorn_conf.py tengo las funciones worker_class = 'gevent' , preload_app = True y personalizadas pre_fork , on_starting y on_reload .

Poner estas líneas encima del archivo gunicorn_conf.py parece resolver el problema por mí:

import gevent.monkey
gevent.monkey.patch_thread()

Actualizar:
He tenido estas líneas en mi gunicorn.conf. Deshacerse de ellos resolvió el problema sin parches de mono.

import django
django.setup()

Debido a ellos, django inició la conexión antes del parche de mono de Gunicorn.

La solución propuesta de

Voy a cerrar esto y abrir una discusión con la etiqueta de la lista de correo en el hito R20 para parchear al árbitro en gevent. Si tiene problemas relacionados con algo en esta discusión y cree que podría ser un error en Gunicorn, abra un problema. Puede mencionar esto por número, o enlazar a comentarios, si ayuda a agregar contexto, pero en este punto, este es un boleto antiguo con conversaciones en paralelo sobre varias versiones de Gunicorn y diferentes síntomas.

¿Fue útil esta página
0 / 5 - 0 calificaciones