Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


A java.net.SocketException Broken Pipe

Por Paulo Silveira em 19/10/09

Quando começamos a programar com banco de dados, rapidamente aprendemos que devemos sempre usar um pool de conexões para acessa-lo, caso contrário podemos facilmente atrapalhar o bom funcionamento do mesmo, devido o excesso de conexões.

Passamos então a usar um pool de conexões, e ao colocar o sistema em produção, nos deparamos com outro problema: o broken pipe:

java.net.SocketException: Broken pipe
at java.net.SocketOutputStream.socketWrite0(Native Method)
at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
at java.net.SocketOutputStream.write(SocketOutputStream.java:136)
at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:2690)
...

No GUJ, são mais de 100 mensagens a respeito de broken pipes! Recentemente Tomaz Lavieri abriu um detalhado tópico sobre esse mesmo assunto, que me incentivou a escrever esse post, dada sua relevância.

Por que essa exception acontece? São dois motivos principais:

O primeiro é que muitos bancos de dados possuem um timeout para conexões inativas (o padrão do MySQL é de 8 horas, mas em alguns hosts isso pode estar configurado em segundos!). Depois de determinado tempo, o banco de dados mata essa conexão ociosa numa tentativa de economizar recursos, pois conclui que alguém simplesmente a esqueceu aberta. Quando, no lado do cliente, seu pool decide usá-la, a socket rapidamente percebe que a conexão foi fechada do outro lado, foi quebrada (daí o nome broken pipe). Muito comum ao começar a usar um pool!

O segundo motivo é que você pode estar tratando suas transações sem o devido cuidado: esquecendo de fazer o commit ou o rollback em alguns casos. O banco de dados então pode matar essa conexão depois de algum timeout de transação, porém o seu pool não sabe disso, e quando for utilizar essa conexão, ela está quebrada!

Solução rápida? Configurar o seu pool para testar se as conexões continuam válidas. No C3P0, pool de conexões que recomendamos fortemente, quando usado com o Hibernate, basta fazer no seu hibernate.cfg.xml:

<property name="hibernate.connection.provider_class">
  org.hibernate.connection.C3P0ConnectionProvider
</property>
<property name="hibernate.c3p0.min_size">1</property>
<property name="hibernate.c3p0.max_size">20</property>
<property name="hibernate.c3p0.timeout">30</property>
<property name="hibernate.c3p0.idle_test_period">100</property>

É a configuração hibernate.c3p0.idle_test_period que resolve o broken pipe. Nesse caso o C3P0 fara essa verificação de maneira assíncrona: ele cria threads (3 por padrão) que checam de tanto em tanto tempo (100 segundos nesse caso) se alguma das conexões do pool está inválida (broken pipe é um dos casos). Na existência de uma conexão assim, essa será eliminada do seu pool! Você ainda pode ter um azar muito grande, pois uma conexão pode ter algum problema logo depois que a thread a verificou! Se você quer ter uma confiabilidade de 100% em relação a suas conexões, você pode configurar a variável testConnectionOnCheckout no arquivo c3p0.properties que deve ser colocado no seu classpath. Isso não é muito recomendado, pois toda vez que uma conexão é pega do pool, alguma forma de ping será feito no banco de dados para saber se ela é válida, perdendo um pouco de performance.

Vale lembrar de que isso não é motivo para você se descuidar no tratamento de transações, centralizando isso dentro de um interceptador/filtro que faça o uso correto do try, catch e finally, precavendo-se de qualquer vazamento. Transações, assim como qualquer outro recurso caro (arquivos, conexões, sockets, threads, etc…), deve ter seu ciclo de vida tratado com atenção, de preferência de maneira isolada.

Caso você não use Hibernate, Jerônimo Mozer mostra como usar o C3P0 programaticamente.

Mais detalhes podem ser vistos nas configurações de teste de conexões do C3P0, detalhes do seu funcionamento com o Hibernate e a página do próprio Hibernate sobre esse pool, mas que se encontra um pouco defasada. Também vemos muitos detalhes como esses no capítulo de dia a dia com Hibernate do nosso curso FJ-26.

  • Share/Bookmark

12 Comments »

  1. Outra dica é que o pool de prepared statements pode deixar vazar memoria, basta configurar hibernate.c3p0.max_statements para 0.

    Comment by Paulo Silveira — October 19, 2009 @ 4:55 pm

  2. Ótimo post Paulo.

    Abs,

    Comment by Edvaldo Melo — October 19, 2009 @ 5:31 pm

  3. Opa Paulo!, excelente esse topico.

    broken pipe é um problema muito recorrente, e vejo muita gente enfrentando o problema, graças aos nossos debates consegui chegar a uma solução onde o teste só é feito a cada 100 segundos, anteriormente eu estava usando um teste a cada retirada de conexão do pool.

    Agora ajudando com mais um dica

    Paulo quote
    “pois toda vez que uma conexão é pega do pool, alguma forma de ping será feito no banco de dados para saber se ela é válida, perdendo um pouco de performance”.

    O padrão do c3p0 para checar a conexão é usar um getTables() que é compativel com todos os bancos de dados, para quem tem muitas tabelas, e esta muito preucupado com a performance, pode reduzir esse teste de três maneiras:

    – configurar o “automaticTestTable”, pode ser configurado para o c3p0 criar uma tabela e realizar testes nela.

    – configurar o “preferredTestQuery” para realizar uma query simples personalizada.

    – criar uma classe que realiza o teste “connectionTesterClassName”

    mais informações aqui => http://bit.ly/VuRFU

    testes.

    Comment by Tomaz Lavieri — October 19, 2009 @ 6:19 pm

  4. Outra dica para quem usa MySQL:

    algumas configurações do banco afetam diretamente neste controle do pool, a maior parte dos meus problemas na configuração do c3p0 foi algumas propriedade do meu servidor MySQL.

    segue as antigas configuraçẽos do my.cnf do servidor que fui obrigado a mudar:

    [my.cnf]
    interactive_timeout=10
    wait_timeout=20
    connect_timeout=20

    “interactive_timeout” e “wait_timeout” são muito parecidas, e as duas matarão conexões inativas por X segundos, onde X é o tempo nelas definidas.

    “connect_timeout” é o tempo maximo que alguem pode ficar conectado ao seu banco de dados, esta configuração definitivamente não estava boa.

    modifiquei o wait e o interactive para 500, e retirei o connect_timeout (o padrão do mysql são 12 horas)

    com essas mudanças meu banco passou a permitir conexões ativar por no maximo de 12 horas, desde que elas sejam renovadas a cada 500 segundos (8,3 minutos).

    como o “idle_test_period” testa a conexão a cada 100 segundos, a conexão do pool é sempre renovada, garantido que não haverá broken, após 12 horas a conexão é perdida devida ao tempo maximo de conexão com o banco, mas como o c3p0 faz um teste a cada 100 segundos, o teste coincide com o momento da queda, reativando instantaneamente.

    Comment by Tomaz Lavieri — October 19, 2009 @ 6:32 pm

  5. Como sempre, uma ótima explicação Paulo.

    []´s

    Comment by Rodrigo Facholi — October 21, 2009 @ 10:11 am

  6. Paulo,

    eu tenho visto por aí o pessoal colocar o idle_test_period igual o timeout, pelo que entendi, com o timeout menor ainda existe um tempo até rolar o teste em que o pool pode acabar pegando uma conexão inválida.

    entendo que nesse caso o timeout é tão pequeno que essa abordagem poderia gerar o mesmo tipo de efeito colateral que o testConnectionOnCheckout.

    será que um timeout maior, por exemplo 100 segundos, traria algum problema? pois neste caso tenho a impressão que já se tornaria viável colocar o mesmo valor para o idle_test_period.

    Comment by Roberto — October 22, 2009 @ 3:35 pm

  7. Oi Roberto!

    Perfeito comentário. Na própria documentação o pessoal do C3P0 recomenda um valor alto para testes, como de 1000 segundos ou mais, para ter uma senhora escalabilidade. Claro que ai voce deve pensar melhor se seu host for compartilhado e conexoes viverem pouco!

    abracos

    Comment by Paulo Silveira — October 22, 2009 @ 3:45 pm

  8. Muito bom o artigo Paulo.
    Hoje comecei o dia com um Broke Pipe e o Jeveaux já me indicou seu artigo.

    Abraço.

    Comment by Washington Botelho — January 29, 2010 @ 9:42 am

  9. Aqui tem mais informações desse problema: http://www.databasesandlife.com/automatic-reconnect-from-hibernate-to-mysql/

    Comment by Paulo Silveira — February 28, 2010 @ 6:36 pm

  10. Srs, trabalho como administrador Weblogic(8, 9 e 10 ) numa estrutura com muitos domínios e muitas aplicações distintas usando pools com bancos Oracle. Gostaria de saber de vocês como deveria proceder para saber o porquê de tantas ocorrências de Broken pipe nos logs das JVM’s. Quero fazer uma pesquisa para ter evidências técnicas para reportar de maneira precisa o problema. Alguém sabe por onde começar ?? Desde já muito obrigado.

    Comment by Edson Marques Barbosa — June 8, 2010 @ 7:02 pm

  11. Ola Edson

    O problema é que alguma das duas partes fechou o seu lado da conexão. provavelmente a não java, e aí quando o Java tenta conversar, o cano estava quebrado. A idéia é usar um bom pool, seja através de datasource ou não, que saiba verificar se a conexao esta boa… o C3P0 faz isso bem.

    abracos

    Comment by Paulo Silveira — June 8, 2010 @ 8:54 pm

  12. [...] A java.net.SocketException Broken Pipe [...]

    Pingback by Camilo Lopes – LpJava » Solucionando Communication link failure Hibernate com Pool + Mysql — July 7, 2010 @ 4:22 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment




Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222