<p>gunicorn 19.x causant des problèmes avec django-1.7.1 et gevent</p>

Créé le 4 nov. 2014  ·  53Commentaires  ·  Source: benoitc/gunicorn

Bonjour,

Je vois cette erreur avec gunicorn 19.x (testé avec 19.0 et 19.1.1 ) en utilisant le travailleur gevent . J'utilise django-1.7.1 .

c'est l'exception :

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.

Voici mon 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"

L'erreur se produit à chaque fois, le simple fait d'accéder à n'importe quelle URL de l'application Django (qui dépend de n'importe quel modèle, bien sûr) déclenche cette erreur. Des idées?
Puisque le passage à gunicorn-18.0 résout le problème, je suppose que gunicorn-19.x fait quelque chose de différent.

Si vous avez besoin d'informations supplémentaires, faites-le moi savoir et je les publierai ici.

Pour l'instant, j'utilise 18.0 .

Merci beaucoup.

( FeaturWorker FeaturCore ThirdpartGevent Deploy Investigation - Bugs -

Commentaire le plus utile

Le correctif proposé par

Tous les 53 commentaires

Juste une question d'abord : est-ce que retirer l'option de préchargement est utile ?
Je pense que je me souviens d'un autre problème similaire signalé.
Si changer cette option aide, alors nous devons avoir changé quelque chose sur la façon dont les applications Django sont chargées et il sera plus facile pour nous de le retrouver.

Je pense que nous importons l'application Django wsgi dans le processus maître, qui, au moins sur Django 1.7, initialise tous les modèles Django lors de l'importation.

Bonjour @tilgovi , merci pour votre temps. Je ne peux pas confirmer qu'en utilisant preload_app = False je peux utiliser gunicorn-19.1.1 et django-1.7.1 .

Avez-vous une idée de ce que gunicorn cloud fait différemment à ce sujet dans les versions 19.x ?

J'ai maintenant un deuxième problème. J'utilise Raven[1] dans mon application Django. Et selon la documentation de Raven, lors de l'utilisation d'un middleware WSGU[1], il est possible d'utiliser Raven en tant qu'objet WSGI (enveloppant l'application django WSGI d'origine) et lors de l'utilisation de gunicorn (en tant que commande externe, pas le run_gunicorn django command) nous devons ajouter un hook à gunicorn[2]. Lorsque ce hook est activé, gunicorn ne démarre que si preload_app = True , mais alors gevent ne fonctionne pas. Lorsque j'utilise preload_app = True avec le crochet activé, gunicorn ne démarre même pas, me donnant et ImportError disant qu'il ne peut pas importer mon "DJANGO_SETTINGS_MODULE".

Le corbeau est 4.2.3

Pour l'instant, je reviens à gunicorn 18.x , car il fonctionne avec les deux configurations (gevent et raven en tant que middleware WSGI)
Dois-je ouvrir un autre problème à ce sujet ?

[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 façon dont les bases de données sont configurées dans django 1.7.1 a probablement changé n'est apparemment pas threadsafe. Je ne sais pas quoi faire à ce moment-là.

Encore une chose, que je viens de découvrir aujourd'hui,
la gestion de session dans Django 1.7 avec gunicorn 19.3.0 expire en quelques secondes.

J'ai dû revenir à 18 pour que cela fonctionne.

POUR VOTRE INFORMATION.

Cela semble être le même problème que https://github.com/benoitc/gunicorn/issues/879 , qui a été fermé mais pas corrigé. Il fait gunicorn 19.x. inutilisable avec Django. Peut-être seulement pour le travailleur de gevent? Je n'ai pas testé pour d'autres types de travailleurs.

@tilgovi je me demande si ce n'est pas dû au changement effectué dans l'environnement. en 19.x, nous définissons wsgi.multithread à true où il était false en 18.x :

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

Ce changement peut être géré différemment dans Django. Et un greenlet n'est pas vraiment un fil. Les pensées? Au moins, nous pourrions peut-être essayer de le rétablir et vérifier si cela résout le problème. Avons-nous un test ou un moyen reproductible pour ce problème?

Oh, je parie que c'est le problème. Bon souvenir, @benoitc.

Je ne sais toujours pas pourquoi. Comme je l'ai dit à l'époque pour justifier le changement, je ne peux pas imaginer que cela ferait quelque chose de cassé car cela signifierait que le cadre ne prendrait que plus de précautions concernant le partage des données. Evidemment, je peux me tromper.

J'ai poussé la branche fix/927 pour vérifier si le correctif ci-dessus fonctionne. S'il vous plaît tester!

Je peux toujours reproduire cette erreur en utilisant fix/927 avec django 1.8.1 (et 1.7.1) et gevent 1.0.1

Cela me rassure, même si nous n'en connaissons toujours pas la cause.

J'ai donc essayé d'utiliser l'exemple d'application et je n'ai pas reproduit le problème en exécutant la ligne de commande suivante :

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

Quelqu'un peut-il me fournir un exemple reproductible?

Je peux toujours reproduire ce problème en invoquant gunicorn comme ceci:

    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

Avez-vous besoin de détails pertinents ?

@brockhaywood devrait être project.settings , mais oui, j'ai besoin d'un moyen de le reproduire, avez-vous un code minimal à partager.

En regardant la trace ci-dessus, cela pourrait aussi être le patch de singe. Peut-être que les threads ne devraient pas être patchés. Peux-tu essayer le patch suivant ?

--- 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()

L'utilisation du correctif ci-dessus avec le wsgi suivant ne produit plus l'erreur spécifiée :

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

Je vois maintenant une erreur différente et nouvelle, mais je ne sais pas si elle est liée :

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

Cela se produit, 1 requête sur 10/15 effectue un décompte sur un modèle. Je soupçonne qu'il s'agit d'une erreur dans mon application qui est maintenant découverte.

@brockhaywood cool ! d'ailleurs c'est ta version de gevent ? Pour votre dernière erreur, cela ne semble pas être lié au gunicorn en effet.

Ouais, bonne nouvelle ! Sur la version 1.0.1 de gevent.

@tilgovi donc je me demande si nous ne devrions pas patcher gunicorn en supprimant le patch de singe de fil. Peut-être devrions-nous également définir wsgi.multithread sur False ? Les pensées?

@benoitc Je suis capable de reproduire la nouvelle erreur que j'ai référencée ci-dessus avec une application Django très simple qui n'a qu'une seule vue et effectue une requête de modèle simple et compte si j'exécute plusieurs requêtes simultanées.

Avez-vous des suggestions quant à quel composant est le coupable le plus probable? Est-ce probablement un problème de gevent ou de psycogreen ?

Plus probablement psycogreen. Utilisez-vous un tel crochet pour le configurer?

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

Oh, j'utilise en fait

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

Je vais essayer ta proposition.

Ok, je vais essayer d'ouvrir un ticket avec psycogreen

pouvez-vous essayer d'utiliser le crochet post_worker_init au lieu de post_fork ?

Cela se produit toujours pour moi lors de la correction de psycogreen dans post_worker_init.

J'ai également envoyé un ticket à psycogreen :
https://bitbucket.org/dvarrazzo/psycogreen/issue/2/databaseerror-used-with-asynchronous-query

@brockhaywood OK :/ https://bitbucket.org/dvarrazzo/psycogreen-hg/issue/1/databaseerror-execute-used-with . Je ne sais pas si cela pourrait vous aider cependant.

J'ai jeté un coup d'œil à cela et cela semble être le même problème, mais je n'ai malheureusement pas vu de résolution.

@brockhaywood pouvez-vous partager un exemple simple de code qui pourrait m'aider à reproduire le problème ? Je vais y jeter un œil demain.

Voici le projet simple que j'utilise pour reproduire :
https://github.com/brockhaywood/gunicorn_gevent_issue

Merci!

Le dimanche 17 mai 2015 à 22:34 brockhaywood [email protected]
a écrit:

Voici le projet simple que j'utilise pour reproduire :
https://github.com/brockhaywood/gunicorn_gevent_issue

-
Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/benoitc/gunicorn/issues/927#issuecomment-102852497 .

@benoitc des idées à ce sujet?

@brockhaywood désolé je n'ai pas eu le temps de le tester. Puisque je suis enfermé dans un vol demain, je le ferai quand même :)

Merci
Le 26 mai 2015 13h17, "Benoit Chesneau" [email protected] a écrit :

@brockhaywood https://github.com/brockhaywood désolé je n'ai pas eu le temps
pour le tester. Puisque je suis enfermé dans un vol demain, je le ferai quand même :)

-
Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/benoitc/gunicorn/issues/927#issuecomment -105652947.

Je venais de rencontrer le même problème DatabaseWrapper quand j'ai vu ce fil.

Je peux confirmer que la suppression de l'option preload_app de mon fichier de configuration gunicorn résout le problème. J'utilise:

  • Django==1.7.7
  • gevent==1.0.2
  • gunicorne==19.3.0
  • psycopg2==2.5.2
  • psycovert==1.0
  • gunicorn enveloppé par PgBouncer

(sautant depuis que nous avons vu les mêmes problèmes que ceux décrits dans ce fil)

Il semble que ces problèmes soient causés par le correctif du problème n ° 478 (ne pas patcher le maître).

En ne corrigeant pas le maître, le préchargement de l'application crée fondamentalement une incompatibilité si vous utilisez des travailleurs gevent. Tout code d'application qui s'exécute dans le cadre du préchargement s'exécute dans un environnement qui n'est pas corrigé par Monkey. Tout code d'application qui s'exécute dans le travailleur s'exécute dans un environnement patché Monkey.

Pour nous, juste restaurer le setup méthode pour le gevent catégorie des travailleurs comme il est défini dans 18.x, de sorte que le maître est singe patché et il est singe patché avant la pré - charge de l' application, fixe tous nos problèmes. Cela inclut le bogue DatabaseWrapper , ainsi qu'un autre problème que nous voyions où les travailleurs de gevent se bloquent et expirent. Aucune autre modification n'a été nécessaire (nous n'avons pas besoin de désactiver le patch Monkey threads , par exemple).

J'hésiterais toujours beaucoup à patcher le processus maître, mais si vous voulez le faire, vous pourrez peut-être utiliser les crochets du serveur au lieu de patcher gunicorn lui-même. Si vous pouvez le faire, je serais heureux que nous puissions documenter cela quelque part qui soit mieux qu'ici.

Je pense que le plan à court terme pour nous est de créer une fourchette de gunicorn (interne) avec ce patch appliqué.

Je vais étudier le déplacement d'une étape de patch singe dans notre code wsgi, mais cela semble toujours être quelque chose qui devrait être fait dans le code gunicorn (même en tant qu'option configurable). Le correctif dans le code d'application préchargé affectera le maître d'une manière ou d'une autre, et l'essentiel serait de maintenir l'état patché singe des travailleurs synchronisé avec le maître. L'application du patch maître singe dans le code de l'application signifie que les développeurs d'applications qui dépendent de ce comportement rattraperont toujours les changements apportés par gunicorn au moment et à la manière dont il corrige le travailleur.

Si le patch fonctionne tôt, cela devrait peut-être être considéré comme un changement.

Je ne me souviens pas si cela a déjà été le cas et si cela a causé des problèmes, mais c'est toujours délicat avec le patch de singe.

Notre propre application de production gunicorn nécessite un préchargement et un patch singe dans le maître (nous utilisons le script pserve Pyramid/paste pour lancer l'application et charger gunicorn à partir d'un fichier ini ; nous avons un wrapper autour d'elle pour être assurez-vous que le patch de singe se produit dès que possible).

Je suis un mainteneur actuel de gevent. Je suis heureux d'examiner tous les problèmes nécessaires à l'amélioration de ce scénario. Nous prévoyons de supprimer la version 1.1 bientôt et ce serait formidable si c'était une option prise en charge.

J'ai essayé d'appliquer la même logique de patch singe en haut de notre implémentation wsgi, et nous avons commencé à voir le problème DatabaseWrapper . Je ne sais pas tout à fait pourquoi - en regardant l'invocation wsgi dans arbiter.py , cela se produit quelques lignes après le point où GeventWorker.setup sera appelé (comme effet secondaire de l' affectation worker_class . Si je lis le code à droite.

Je ferai un suivi si j'ai un eureka ou si j'ai de la chance en ce qui concerne le code de l'application, mais jusqu'à présent, pour nous, le correctif plus tôt dans le code principal de gunicorn est la seule solution qui résout tous nos problèmes.

@jamadden merci beaucoup.

@jzupko, il est généralement important que le correctif se produise avant toute importation susceptible d'être affectée. C'est pourquoi j'ai suggéré de le faire dans un hook de serveur, comme en mettant une fonction post_fork dans un fichier gunicorn.conf.py . Cependant, je viens de réaliser que le préchargement de l'application se produit avant l'un des crochets du serveur et il n'est pas clair où l'on ferait cela.

@benoitc avons-nous un mécanisme pour la configuration spécifique au travailleur ?

J'ajoute cela au jalon de la version 20 et suggère que nous envisagions de patcher le maître, peut-être, si cela peut être fait sans réintroduire des problèmes comme le #478.

@tilgovi vous avez la méthode setup du travailleur qui peut être utilisée pour cela je pense.

Quelqu'un a-t-il un correctif temporaire pour cela jusqu'à la sortie de 20.0 ? Ou est la seule solution à choisir entre désactiver preload_app ou rétrograder à 18.0 ?

@fletom correctif temporaire pour ? Avez-vous essayé le dernier maître? Cela a-t-il fonctionné pour vous ?

@benoitc Je veux dire un correctif pour l'erreur "Objects DatabaseWrapper".

Le maître gunicorn actuel ne fonctionne pas du tout pour moi en fait. Sur 19.6.0 régulier, j'obtiens ceci (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

Et sur le dernier maître avec la même configuration à 100%, j'obtiens ceci, et rien n'écoute sur aucun port pour autant que je sache :

[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

Semble buggé?

@fletom comment

@benoitc Dans ce cas, c'est gunicorn -c gunicorn_conf.py <appname>.wsgi .

Mon gunicorn_conf.py est :

workers = 4

worker_class = 'gevent'

preload_app = False

Je tombe sur ce problème avec gunicorn==18.0 . Dans le fichier gunicorn_conf.py, j'ai des fonctions worker_class = 'gevent' , preload_app = True et personnalisées pre_fork , on_starting et on_reload .

Mettre ces lignes au-dessus du fichier gunicorn_conf.py semble résoudre le problème pour moi :

import gevent.monkey
gevent.monkey.patch_thread()

Mettre à jour:
J'ai eu ces lignes dans mon gunicorn.conf. Se débarrasser d'eux a résolu le problème sans patch de singe.

import django
django.setup()

À cause d'eux, Django a initié la connexion avant le patch de singe de gunicorne.

Le correctif proposé par

Je vais fermer ceci et ouvrir une discussion avec l'étiquette de la liste de diffusion sur le jalon R20 pour patcher le singe de l'arbitre à gevent. Si vous rencontrez des problèmes liés à quoi que ce soit dans cette discussion et que vous pensez qu'il pourrait s'agir d'un bogue dans Gunicorn, veuillez ouvrir un problème. Vous pouvez mentionner celui-ci par un numéro, ou un lien vers des commentaires, si cela aide à ajouter du contexte, mais à ce stade, il s'agit d'un ancien ticket avec des conversations en parallèle sur plusieurs versions de Gunicorn et différents symptômes.

Cette page vous a été utile?
0 / 5 - 0 notes