Estou executando um aplicativo Python Pyramid em um servidor CentOS usando uWSGI e nginx. Estou usando SQLAlchemy como um ORM, MySQLdb como API e MySQL como banco de dados. O site ainda não foi lançado, então o único tráfego sou eu e alguns outros funcionários da empresa. Como adquirimos alguns dados para preencher o banco de dados, a maior (e mais frequentemente consultada) tabela é de ~ 150.000 linhas.
Ontem, abri quatro novas guias do site em rápida sucessão e recuperei alguns erros 502 Bad Gateway. Procurei no log do uWSGI e encontrei o seguinte:
sqlalchemy.exc.OperationalError: (OperationalError) (2006, 'MySQL server has gone away') 'SELECT ge...
Nota importante: Este erro não se deve ao wait_timeout do MySQL. Estive lá, fiz isso.
Gostaria de saber se o problema foi causado por solicitações simultâneas sendo atendidas simultaneamente. Fiz para mim um testador de carga de pobre:
for i in {1..10}; do (curl -o /dev/null http://domain.com &); done;
Certamente, dentre essas dez solicitações, pelo menos uma geraria um erro de 2006, muitas vezes mais. Às vezes, os erros ficavam ainda mais estranhos, por exemplo:
sqlalchemy.exc.NoSuchColumnError: "Could not locate column in row for column 'table.id'"
Quando a coluna definitivamente existe e funcionou bem em todos os outros pedidos idênticos. Ou este:
sqlalchemy.exc.ResourceClosedError: This result object does not return rows. It has been closed automatically.
Quando, mais uma vez, funcionou bem para todos os outros pedidos.
Para verificar ainda mais se o problema surgiu de conexões simultâneas com o banco de dados, configurei o uWSGI para um único trabalhador e o multithread desativado, forçando as solicitações a serem processadas uma por vez. Com certeza, os problemas desapareceram.
Na tentativa de encontrar o problema, configurei um log de erros para o MySQL. Com exceção de alguns avisos durante a inicialização do MySQL, ele permanece vazio.
Aqui está minha configuração do MySQL:
[mysqld]
default-storage-engine = myisam
key_buffer = 1M
query_cache_size = 1M
query_cache_limit = 128k
max_connections=25
thread_cache=1
skip-innodb
query_cache_min_res_unit=0
tmp_table_size = 1M
max_heap_table_size = 1M
table_cache=256
concurrent_insert=2
max_allowed_packet = 1M
sort_buffer_size = 64K
read_buffer_size = 256K
read_rnd_buffer_size = 256K
net_buffer_length = 2K
thread_stack = 64K
innodb_file_per_table=1
log-error=/var/log/mysql/error.log
Pesquisando profundamente no erro revelou pouco, mas sugeri que eu aumentasse max_allowed_packet. Aumentei para 100M e reiniciei o MySQL, mas isso não ajudou em nada.
Resumindo: Conexões simultâneas com o MySQL causam 2006, 'MySQL server has gone away'
e outros erros estranhos. Não há nada de relevante no log de erros do MySQL.
Trabalho nisso há horas e não fiz nenhum progresso. Alguém pode me ajudar?
Respostas:
Encontrei isso também e encontrei o motivo e a correção.
A razão pela qual isso acontece é que o plugin python uwsgi (ou, mais provavelmente, todos os plugins uwsgi) fork () novos trabalhadores depois que o aplicativo é carregado no pai. Como resultado, os filhos herdam todos os recursos (incluindo descritores de arquivo como a conexão db) do pai.
Você pode ler sobre isso brevemente no wiki do uwsgi :
E como você deve saber, as conexões e cursores do mysqldb do Python não são seguros para threads, a menos que você os proteja explicitamente. Portanto, vários processos (como os trabalhadores do uwsgi) usando a mesma conexão / cursor do mysql simultaneamente o corrompem.
No meu caso (para a API Gold do King Arthur ), isso funcionou bem quando criei a conexão MySQL por solicitação no escopo de outro módulo, mas quando desejei conexões persistentes para ajudar no desempenho, movi a conexão e o cursor do banco de dados para o escopo global em o módulo pai. Como resultado, minhas conexões se interpuseram como as suas.
A correção para isso é adicionar a palavra-chave "preguiçosa" (ou a opção de linha de comando - preguiçosa) à sua configuração do uwsgi. Como resultado, o aplicativo será bifurcado novamente para cada filho, em vez de bifurcar-se dos pais e compartilhar a conexão (e pisar nela em algum momento, de modo que o servidor MySQL o force a fechar devido a uma solicitação corrompida em algum momento).
Por fim, se você quiser uma maneira de fazer isso sem modificar sua configuração do uwsgi, provavelmente poderá usar o decorador @postfork para criar corretamente uma nova conexão com o banco de dados imediatamente após a bifurcação de um processo de trabalho. Você pode ler sobre isso aqui .
Vejo pelo seu acompanhamento que você já mudou para o pgsql, mas aqui está a resposta para que você possa dormir melhor à noite e para alguém como você e eu tentando encontrar a resposta para isso!
PS: Depois de entender o problema (o cursor está sendo corrompido devido aos trabalhadores pisarem um no outro), mas não percebi o pouco sobre fork () e --lazy, estava pensando em implementar meu próprio pool onde os trabalhadores " verifique "uma conexão mysql de um pool no escopo global e, em seguida," faça check-in novamente antes de sair do aplicativo (), no entanto, provavelmente é muito melhor usar --lazy, a menos que a carga da sua aplicação / web varie o suficiente para que você esteja constantemente criando novos trabalhadores. Mesmo assim, eu poderia preferir --lazy porque é significativamente mais limpo do que implementar seu próprio pool de conexões db.
edit: aqui está uma descrição mais detalhada desse problema + solução, pois há uma escassez de informações sobre ele para outras pessoas que o encontraram: http://tns.u13.net/?p=190
fonte
ping
e para outrosmysqladmin
pedidos. Provavelmente foi porque eu estava tentando soltar o banco de dados do shell ... mas continuava dando o erro "o servidor foi embora" a esse comando. De qualquer forma, obrigado!