<p>gunicorn 19.x causando problemas com django-1.7.1 e gevent</p>

Criado em 4 nov. 2014  ·  53Comentários  ·  Fonte: benoitc/gunicorn

Olá,

Estou vendo este erro com o gunicorn 19.x (testado com 19.0 e 19.1.1 ) usando o trabalhador gevent . Estou usando django-1.7.1 .

esta é a exceção:

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.

Aqui está meu 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"

O erro ocorre todas as vezes, apenas acessar qualquer URL do app django (que depende de qualquer modelo, é claro) já aciona este erro. Alguma ideia?
Como mudar para gunicorn-18.0 resolve o problema, estou assumindo que gunicorn-19.x está fazendo algo diferente.

Se você precisar de alguma informação adicional, me avise e eu a postarei aqui.

Por enquanto estou usando 18.0 .

Muito obrigado.

( FeaturWorker FeaturCore ThirdpartGevent Deploy Investigation - Bugs -

Comentários muito úteis

A correção proposta de

Todos 53 comentários

Antes de mais nada, só uma pergunta: retirar a opção de pré-carga ajuda?
Acho que me lembro de outro problema semelhante sendo relatado.
Se mudar essa opção ajudar, então devemos ter mudado algo sobre como os aplicativos Django são carregados e será mais fácil para nós rastreá-lo.

_Acho_ que importamos o aplicativo django wsgi no processo mestre, que pelo menos no django 1.7, inicializa todos os modelos django na importação.

Olá @tilgovi , obrigado pelo seu tempo. Não posso confirmar que usando preload_app = False consigo usar gunicorn-19.1.1 e django-1.7.1 .

Você tem alguma ideia do que a nuvem gunicorn está fazendo de diferente sobre isso nas versões 19.x ?

Agora tenho um segundo problema. Eu uso Raven [1] em meu aplicativo django. E de acordo com os documentos Raven, ao usar um middleware WSGU [1] é possível usar Raven como um objeto WSGI (envolvendo o aplicativo WSGI django original), e ao usar gunicorn (como um comando externo, não o run_gunicorn django command) devemos adicionar um gancho ao gunicorn [2]. Quando este gancho está habilitado, o gunicorn só inicializa se preload_app = True , mas o gevent não funciona. Quando eu uso preload_app = True com o gancho habilitado, o gunicorn nem mesmo inicializa, me dando e ImportError dizendo que não pode importar meu "DJANGO_SETTINGS_MODULE".

Raven é 4.2.3

Por enquanto, voltarei ao gunicorn 18.x , uma vez que funciona com ambas as configurações (gevent e raven como um middleware WSGI)
Devo abrir outra edição sobre isso?

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

@daltonmatos a maneira como os bancos de dados são configurados no django 1.7.1 provavelmente mudou, aparentemente não são threadsafe. Não tenho certeza do que fazer nesse ponto.

Mais uma coisa, descobri hoje,
manipulação de sessão no django 1.7 com gunicorn 19.3.0 expira em segundos.

Tive que voltar para 18 para fazer funcionar.

PARA SUA INFORMAÇÃO.

Este parece ser o mesmo problema que https://github.com/benoitc/gunicorn/issues/879 , que foi fechado, mas não corrigido. Faz o gunicorn 19.x. inutilizável com Django. Talvez apenas para o trabalhador gevent? Não testei outros tipos de trabalhadores.

@tilgovi gostaria de saber se não é devido à mudança de ambiente. em 19.x definimos wsgi.multithread para true onde estava false em 18.x:

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

Esta mudança pode ser tratada de forma diferente no Django. E um greenlet não é realmente um fio. Pensamentos? Pelo menos talvez possamos tentar reverter e verificar se resolve o problema. Temos um teste ou uma forma reproduzível para esse problema?

Oh, aposto que esse é o problema. Boa memória, @benoitc.

Eu ainda não sei por quê. Como disse na época para justificar a mudança, não consigo imaginar que isso quebrasse alguma coisa, pois isso significaria que o framework só tomaria mais precauções quanto ao compartilhamento de dados. Evidentemente, posso estar errado.

Eu empurrei o branch fix/927 para verificar se a correção acima funciona. Por favor, teste!

Ainda consigo reproduzir este erro usando fix / 927 com django 1.8.1 (e 1.7.1) e gevent 1.0.1

Isso é reconfortante para mim, mesmo que ainda não saibamos a causa.

Então, tentei usar o aplicativo de exemplo e não reproduzi o problema executando a seguinte linha de comando:

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

Alguém pode me fornecer um exemplo reproduzível?

Ainda posso reproduzir esse problema invocando o gunicorn assim:

    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

Existe algum detalhe relevante de que você precisa?

@brockhaywood deve ser project.settings , mas sim, eu preciso ter uma maneira de reproduzi-lo, você tem algum código mínimo para compartilhar.

Olhando para o traço acima, também pode ser o remendo do macaco. Talvez os tópicos não devam ser corrigidos. Você pode tentar o seguinte patch?

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

Usar o patch acima com o seguinte wsgi não produz mais o erro 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

Agora estou vendo um erro diferente e novo, mas não tenho ideia se está relacionado:

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

Isso está ocorrendo, 1 em 10/15 solicitações está fazendo uma contagem em um modelo. Suspeito que seja um erro em meu aplicativo que agora foi descoberto.

@brockhaywood cool! btw ahat é a sua versão do gevent? Para o seu último erro, ele não parece relacionado ao gunicorn de fato.

Sim, boas notícias! Na versão 1.0.1 do gevent.

@tilgovi, então eu me pergunto se não deveríamos remendar o gunicorn removendo o patching monkey. Talvez devêssemos também definir wsgi.multithread para False ? Pensamentos?

@benoitc Consigo reproduzir o novo erro que mencionei acima com um aplicativo Django muito simples que tem apenas uma visualização e faz uma consulta de modelo simples e conta se eu executar várias solicitações simultâneas.

Você tem alguma sugestão sobre qual componente é o culpado mais provável? É provável que seja um problema gevent ou psycogreen?

Mais provavelmente psicogreen. Você está usando esse gancho para configurá-lo?

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

Oh, na verdade estou usando

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

Vou tentar sua sugestão.

Ok, vou tentar abrir um tíquete com psycogreen

você pode tentar usar o gancho post_worker_init vez de post_fork ?

Ainda ocorre para mim ao corrigir psycogreen em post_worker_init.

Também enviei um tíquete para psycogreen:
https://bitbucket.org/dvarrazzo/psycogreen/issue/2/databaseerror-used-with-asynchronous-query

@brockhaywood OK: / Também encontrei este link: https://bitbucket.org/dvarrazzo/psycogreen-hg/issue/1/databaseerror-execute-used-with . Não tenho certeza se isso poderia ajudá-lo.

Dei uma olhada nisso e parece que é o mesmo problema, mas não vi uma solução, infelizmente.

@brockhaywood você pode compartilhar algum exemplo simples de código que poderia me ajudar a reproduzir o problema? Vou dar uma olhada nisso amanhã.

Aqui está o projeto simples que estou usando para reproduzir:
https://github.com/brockhaywood/gunicorn_gevent_issue

obrigado!

No domingo, 17 de maio de 2015 às 22h34 brockhaywood [email protected]
escreveu:

Aqui está o projeto simples que estou usando para reproduzir:
https://github.com/brockhaywood/gunicorn_gevent_issue

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/benoitc/gunicorn/issues/927#issuecomment -102852497.

@benoitc alguma opinião sobre isso?

@brockhaywood desculpe, não tive tempo para testá-lo. Já que estou preso em um vôo amanhã, vou de qualquer maneira :)

Obrigado
Em 26 de maio de 2015, 13h17, "Benoit Chesneau" [email protected] escreveu:

@brockhaywood https://github.com/brockhaywood desculpe, não tive tempo
para testá-lo. Já que estou preso em um vôo amanhã, vou de qualquer maneira :)

-
Responda a este e-mail diretamente ou visualize-o no GitHub
https://github.com/benoitc/gunicorn/issues/927#issuecomment -105652947.

Estava enfrentando o mesmo problema de DatabaseWrapper quando vi este tópico.

Posso confirmar que remover a opção preload_app do meu arquivo de configuração do gunicorn corrige o problema. Estou a usar:

  • Django == 1.7.7
  • gevent == 1.0.2
  • gunicorn == 19.3.0
  • psycopg2 == 2.5.2
  • psycogreen == 1.0
  • gunicorn embrulhado por PgBouncer

(entrando em ação, já que vimos os mesmos problemas descritos neste tópico)

Parece que esses problemas estão sendo causados ​​pela correção do problema nº 478 (não faça monkey patch no master).

Por não fazer o monkey patching no master, a pré-carga do aplicativo cria fundamentalmente uma incompatibilidade se você estiver usando gevent workers. Qualquer código de aplicativo executado como parte do pré-carregamento é executado em um ambiente que não é corrigido pelo macaco. Qualquer código de aplicativo executado no trabalhador é executado em um ambiente com patch de macaco.

Para nós, apenas restaurar o método setup para a classe de trabalho gevent conforme definido em 18.x, de modo que o master tenha o monkey patch e o monkey patch antes do pré-carregamento do aplicativo, consertou tudo nossos problemas. Isso inclui o bug DatabaseWrapper , bem como outro problema que vimos, onde os funcionários do gevent travavam e expiravam. Nenhuma outra mudança foi necessária (não precisamos desabilitar o monkey patching threads , por exemplo).

Eu ainda estaria muito hesitante em fazer um monkey-patch no processo mestre, mas se você quiser fazer isso, você pode usar os ganchos do servidor em vez de fazer o patch do próprio gunicorn. Se você puder fazer isso, eu agradeceria qualquer maneira de documentar isso em algum lugar que seja melhor do que aqui.

Acho que o plano de curto prazo para nós é criar um garfo de gunicorn (interno) com esse patch aplicado.

Vou investigar a mudança de uma etapa de patch monkey em nosso código wsgi, mas isso ainda parece algo que deve ser feito dentro do código gunicorn (mesmo como uma opção configurável). A correção no código do aplicativo pré-carregado afetará o mestre de uma forma ou de outra, e o objetivo principal seria manter o estado do macaco corrigido dos workers em sincronia com o mestre. Aplicar o patch do macaco mestre no código do aplicativo significa que os desenvolvedores de aplicativos que dependem desse comportamento estarão sempre tentando acompanhar quaisquer mudanças que o gunicorn fizer em quando / como ele corrige o trabalho.

Se funcionar para corrigir antes, talvez isso deva ser considerado uma mudança.

Não me lembro se foi esse o caso e se isso causou problemas, mas é sempre complicado com o patch de macaco.

Nosso próprio aplicativo de produção de gunicorn requer pré-carregamento e também requer monkey-patching no mestre (usamos o script pserve Pyramid's / paste para iniciar o aplicativo e carregar o gunicorn de um arquivo ini; temos um invólucro ao redor dele para ser certifique-se de que o patching ocorra o mais rápido possível).

Eu sou um mantenedor atual do gevent. Fico feliz em analisar quaisquer problemas no gevent necessários para fazer este cenário funcionar melhor. Esperamos descartar o 1.1 em breve e seria ótimo se essa fosse uma opção com suporte.

Tentei aplicar a mesma lógica de patch monkey no início de nossa implementação wsgi e começamos a ver o problema DatabaseWrapper . Não tenho muita certeza do porquê - olhando para a invocação de wsgi em GeventWorker.setup será chamado (como um efeito colateral da atribuição worker_class . Se eu estiver lendo o código certo.

Farei o acompanhamento se tiver um eureka ou tiver sorte com relação ao código do aplicativo, mas até agora, para nós, corrigir anteriormente no código mestre do gunicorn é a única correção que resolve todos os nossos problemas.

@jamadden muito obrigado.

@jzupko geralmente é importante que a correção aconteça antes de qualquer importação que possa ser afetada. É por isso que sugeri fazer isso em um gancho de servidor, como colocar uma função post_fork em um arquivo gunicorn.conf.py . No entanto, acabei de perceber que o pré-carregamento do aplicativo acontece antes de qualquer um dos ganchos do servidor e não está claro onde fazer isso.

@benoitc temos algum mecanismo para configuração específica do trabalhador?

Estou adicionando isso ao marco do release 20 e sugerindo que consideremos o monkey patching no master, talvez, se isso puder ser feito sem reintroduzir problemas como o # 478.

@tilgovi, você tem o método setup do trabalhador que pode ser usado para isso, eu acho.

Alguém tem uma solução temporária para isso até o lançamento de 20.0? Ou a única solução é escolher entre desabilitar preload_app ou fazer downgrade para 18,0?

correção temporária do

@benoitc Quero dizer uma correção para o erro "Objetos DatabaseWrapper".

O mestre gunicorn atual não funciona para mim, na verdade. No 19.6.0 normal, eu entendo (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

E no último mestre com a mesma configuração 100% exata, recebo isso, e nada ouvindo em nenhuma porta, até onde posso dizer:

[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 grampeado?

@fletom como você inicia o gunicorn?

@benoitc Neste caso, é gunicorn -c gunicorn_conf.py <appname>.wsgi .

Meu gunicorn_conf.py é:

workers = 4

worker_class = 'gevent'

preload_app = False

Estou descobrindo esse problema com gunicorn==18.0 . No gunicorn_conf.py eu tenho worker_class = 'gevent' , preload_app = True e funções personalizadas pre_fork , on_starting e on_reload .

Colocar essas linhas na parte superior do arquivo gunicorn_conf.py parece resolver o problema para mim:

import gevent.monkey
gevent.monkey.patch_thread()

Atualizar:
Tive essas linhas em meu gunicorn.conf. Livrar-se deles resolveu o problema sem remendar macacos.

import django
django.setup()

Por causa deles, Django iniciou a conexão antes do remendo de macacos do gunicorn.

A correção proposta de

Vou fechar isso e abrir uma discussão com o rótulo da lista de e-mails no marco R20 para corrigir o erro do árbitro no gevent. Se você estiver tendo problemas relacionados a qualquer coisa nesta discussão e achar que pode ser um bug no Gunicorn, abra um problema. Você pode mencioná-lo por número ou link para comentários, se ajudar a adicionar contexto, mas neste ponto, este é um tíquete antigo com conversas paralelas sobre várias versões do Gunicorn e sintomas diferentes.

Esta página foi útil?
0 / 5 - 0 avaliações