[RESOLVIDO] c3p0, o site do GUJ usa ? broken pipe soluções?

[EDIT]Bom resumo das soluções usadas podem ser encontrada neste post => http://blog.caelum.com.br/2009/10/19/a-java-net-socketexception-broken-pipe/[/EDIT]

Opa pessoal, aqui no GUJ vocês usam c3p0 ??

andei tendo problemas de brokenpipe algumas poucas vezes essa semana navegando aqui pelo site do GUJ. passei por um perrengue grande la na empresa, pra resolver um problema similar.

Meu servidor MySQL estava configurado para resetar a cada 20s, e então 20 segundos sem ninguem entrar no site, e o pool de conexões do hibernate ia por agua abaixo, ao criar um session o hibernate não verifica se as suas conexão do pool estão realmente abertas, ele apenas pega uma e a devolve, e então quando você tenta usar a session recebe uma exceção de broken pipe, conforme recebi algumas vezes acessando aqui o guj, a exceção ta no final deste post.

Aumentei para 500 segundos, e então configurei o c3p0, para renovar caso houvesse ociosidade por 100 segundos,

[EDIT] as linhas abaixo descobri que não era o caso, o problema era outras configurações do banco, que falo no final deste post, e no post do Paulo Silveira.[/EDIT]
[not-true]o problema é que aparentemente o c3p0 tem uma forma estranha de renovar a conexão, ele renova a que esta ociosa a mais de 100 segundos e renova também todas as outras, então a cada 100 segundos, todo meu pool tomava um reset, gerando mais broken pipe, pois se uma das sessões não ociovas estivesse em uso no momento do reset o usuario recebia um broken pipe no meio da requisição[/not-true]

não sei se foi problema por não entender a configuração do c3p0, mas li diversos artigos configurei igual varios lugares, e o comportamento se mantinha, não conseguindo configurar o c3p0, acabei sendo vencido, e mudei de approche.

envolvi a factory de sessão com um check, para verificar diretanemente na connection se a conexão estava ativa (já que quando a session é criada o hiberante não as checa).

[EDIT]já não estou usando mais esse trecho abaixo, consegui solucionar sem ter que testar a cada conexão que sai do pool, e configurei o c3p0.[/EDIT]

try { if (!session.connection().isValid(1)) { session.reconnect(null); logger.warn("connection was lost, reconnect was made"); } } catch (SQLException ex) { logger.error("fail trying to reconnect to database", ex); throw new PersistenceException("fail trying to reconnect to database",ex); }

A abordagem funciona, sempre que uma conexão expira, eu a recupero-a passando por esse trecho.
Porem fiquei intrigado com esse problema e com esta solução, e tenho medo de ser algo muito POG.

Já fiz diversos teste com este approche, inclusive dando kills na conexão para simular o timeout, e a conexão sempre se recupera.

Gostaria de saber como o GUJ lida com os broken pipe, e como é feito o pool de conexão de vocês.

Segue abaixo a exceção que tive aqui no GUJ, esta exceção é a mesma que recebia no meu servidor quando rolava timeout.

[code]<!-- BEGIN ERROR STACK TRACE
net.jforum.exceptions.ForumException: java.lang.reflect.InvocationTargetException
at sun.reflect.GeneratedMethodAccessor244.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at net.jforum.Command.process(Command.java:114)
at net.jforum.JForum.processCommand(JForum.java:217)
at net.jforum.JForum.service(JForum.java:200)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:487)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1097)
at net.jforum.util.legacy.clickstream.ClickstreamFilter.doFilter(ClickstreamFilter.java:59)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1088)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:360)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:181)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:729)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:405)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:206)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.handler.StatisticsHandler.handle(StatisticsHandler.java:89)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:324)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:505)
at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:843)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:647)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:211)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:380)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:395)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:488)
Caused by: java.lang.reflect.InvocationTargetException
… 29 more
Caused by: net.jforum.exceptions.DatabaseException: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

Last packet sent to the server was 1 ms ago.
at sun.reflect.GeneratedConstructorAccessor50.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:406)
at com.mysql.jdbc.SQLError.createCommunicationsException(SQLError.java:1074)
at com.mysql.jdbc.MysqlIO.send(MysqlIO.java:3246)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1917)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1885)
at com.mchange.v2.c3p0.impl.NewProxyPreparedStatement.executeQuery(NewProxyPreparedStatement.java:76)
at net.jforum.dao.generic.GenericPostDAO.selectById(GenericPostDAO.java:83)
at net.jforum.view.forum.PostAction.editSave(PostAction.java:754)
… 29 more
Caused by: com.mysql.jdbc.exceptions.jdbc4.CommunicationsException: Communications link failure

Last packet sent to the server was 1 ms ago.
… 43 more
Caused by: 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:3227)
… 37 more

URL is: /jforum.java?null
END ERROR STACK TRACE–>[/code]

Ola Lavieri!

Estou adiando de fazer um post sobre broken pipe a tempos… é um assunto muito pertinente!

Primeiro, sobre o GUJ, achei estranho, pq usamos o c3p0. E adivinha? Com a atulizacao do JForum as variaveis estao comentadas!!! Vamos mudar e no proximo restart nao deve haver mais broken pipes. (quer dizer, deve haver, mas o c3p0 nao vai passar essas conexoes pra gente!).

Para acabar com o Broken Pipe com o c3p0 tem dois modos:

1-) checar se a conexao esta viva!
essa é a forma que voce implementou! mas tambem da pra fazer via c3p0:
c3p0.testConnectionOnCheckout=true

toda vez que ele pegar uma conexao emprestada, ele da um ping! a desvantagem é de performance mesmo: toda vez que voce for fazer uma query, vai “tocar” o banco de dados duas vezes. dois roundtrips.

2-) de tempos em tempos, uma thread passa pelo pool inteiro e da o ping!
c3p0.idle_test_period=100 #testa de 100 em 100 segundos se tem alguem morto

é uma forma que economiza bastante, ja que nao vai pingar TODA vez que voce for fazer uma query, . em relacao a primeira proposta, voce perde reliability, ganha performance.

A documentacao do c3p0 dizia que da ate para usar os dois juntos, para algo mais reliable ainda, e obviamente de menor performance. No seu caso particular, creio que voce va precisar da opcao 1, pois o tempo de vida é muito curto de cada conexao!

Cuidado que alguns parametros do c3p0 sao configurados no hibernate com outro nome, e isso mudou de uns tempos pra ca. Entao verifique no log do c3p0 se suas opcoes foram carregadas corretamente!

abracos

bom eu usei a segunda abordagem do

c3p0.idle_test_period=100

ele bizarramente estava resetando todas quando encontrava algum problema, sempre a cada 100s, havia um problema =/

não sei o que é

sobre o

c3p0.testConnectionOnCheckout=true

cheguei a experimentar também, não lembro perfeitamente dos resultados, mas vou testar novamente… mas entre usar o c3p0 resetando a cada consulta, e fazer um recheck a cada abertura de sessão como estou fazendo com meu HiberanteUtils, prefiro fazer no HiberanteUtils, pra não estressar de + o banco.

vou tentar fazer novas checagem no meu servidor.

e para falar a verdade eu tenho acesso a configurações de tempo de time_out do servidor, e posso trocar, mas como não sabia bem qual era a melhor opção, so fiz aumentar de 20s (o que sabia q era muito pouco) para 500s (o que é razoável) … o padrão do mysql são 8 horas o.O aparentemente seria de + …

desde a inserção do código no HiberanteUtils nunca mais houve broken pipe no servidor… já fiz testes do tipo de dar KILL na conexão, e a sessão sempre se recupera, sem maiores estresses… afinal como abro e fecho a sessão a cada requisição (e como agora uso lazy session) ou seja, so abro sessão para quem usa o banco, e só durante seu uso… então ninguem passa mais de 500s com uma sessão aberta, não acontecendo problemas mais de broken

Ola Lavieri!

Fazer session.connection().isValid(1) no seu HibernateUtils estressa o banco da mesma forma que o test_on_checkout… Se der pra deixar num arquivo de configuracao do hibernate em vez do hibernate util, acho uma vantagem, mas nao acho POG o que voce fez, em especial por estar isolado.

[quote=Paulo Silveira]Ola Lavieri!

Fazer session.connection().isValid(1) no seu HibernateUtils estressa o banco da mesma forma que o test_on_checkout… Se der pra deixar num arquivo de configuracao do hibernate em vez do hibernate util, acho uma vantagem, mas nao acho POG o que voce fez, em especial por estar isolado.[/quote]

pelo que tinha entendido do seu post, o c3p0 vai fazer o teste a cada requisição minha do banco…

o meu teste só é realizado na abertura de um Session do hibernate… ele na verdade é assim:

[code]@Component
@RequestScoped
public class JITEntityManagerCreator implements ComponentFactory {
//… outras coisas da classe
@Override
public EntityManager getInstance() {
if (entityManager == null)
entityManager = HibernateUtils.reconnectIfNeed( //reconnectIfNeed é que realiza o teste
factory.createEntityManager() //esse teste só é feito 1 vez na abertura da session
);
return entityManager;
}

@PreDestroy
public void destroy() {
	if (entityManager != null && entityManager.isOpen()) {
		entityManager.close();
	}
}
//mais coisas

[/code]

ou seja, eu só teso uma vez, quando ela é aberta, e como seu escopo é de Request, a session não dura muito tempo…

dentro deste request, se eu tiver 1000 select, ou qualquer outra coisa, não terei novos testes, apenas o primeiro no momento da abertura…

Esse approche esta assim, pois o problema sempre se da, quando passa muito tempo com a conexão no pool, sem nenhuma session usar, durante a session aberta, nunca deu problema. então eu só testo no start…

Se o “test_on_checkout” só checar uma vez, na hora de criar a session ai é igual o stress, mas se checar a cada requisição no banco, então ele vai gerar mais stress …

[quote=Lavieri]
Se o “test_on_checkout” só checar uma vez, na hora de criar a session ai é igual o stress,[/quote]

Praticamente isso. Ele só vai checar quando alguem puxar uma conexao do pool. E isso vai acontecer na hora de abrir uma session!

[quote=Paulo Silveira][quote=Lavieri]
Se o “test_on_checkout” só checar uma vez, na hora de criar a session ai é igual o stress,[/quote]

Praticamente isso. Ele só vai checar quando alguem puxar uma conexao do pool. E isso vai acontecer na hora de abrir uma session![/quote]

teria como postar o c3p0 aqui do guj ???

    <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.max_statements">0</property>
    <property name="hibernate.c3p0.idle_test_period">100</property>
    <property name="hibernate.c3p0.timeout">50</property>

[quote=Paulo Silveira] <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.max_statements">0</property> <property name="hibernate.c3p0.idle_test_period">100</property> <property name="hibernate.c3p0.timeout">50</property> [/quote]

opa! vlw!

ja leu essa parte do tutorial do c3p0 ?? http://www.mchange.com/projects/c3p0/index.html#hibernate-specific

algumas coisas que vc não esta especificando o hibernate esta setando o padrão dele, mesmo que vc não sete.

Ola Tomaz! Desses so nao setamos o aquire_increment, que o default de 1 é suficiente para nós! Os outros estão todos setados ai.

blz, segunda feira, devo fazer uns teste la no trabalho, para ver o comportamento do c3p0

oi tomaz!

depois posta o resultado! to escrevendo um post a respeito!

abracos!

[quote=Paulo Silveira]oi tomaz!

depois posta o resultado! to escrevendo um post a respeito!

abracos!
[/quote]

posto sim, ja configurei antes, inclusive seguindo o modelo que tem no site do hibernate.org … mas continuou rolando os broken pipe, depois que fiz a modificação que postei aqui sobre o HiberanteUtils nunca mais deu…

sobre aqui o guj, não sei c vc já descomentou e restartou, mas hoje recebi + um broken pipe, no periou da tarde

RESOLVIDO, problema foi solucionado

encontrei o resto das configurações que faltavam no meu servidor de mysql

interactive_timeout=10
connect_timeout=20

eu ja tinha mudado o wait_timeout=500 mas faltava as outras duas propriedades. agora o problema foi resolvido.

mais detalhes no post do Paulo Silveira no blog da caelum, onde comento sobre essa propriedade e outros detalhes do c3p0.

http://blog.caelum.com.br/2009/10/19/a-java-net-socketexception-broken-pipe/

Interessante é que, se sabem tudo que pode ser a causa disso, porque ainda recebemos esta mensagem aqui no GUJ frequentemente?

Oi DJ!

Na ultima migracao de versao (2.1.7 pra 2.1.8) do jforum, acabou que passamos a configuracao por cima e ficamos com essas configuracoes desabilitadas, acabamos de restartar o servidor com as configuracoes novas, vamos ver se estao ok agora.

É só 20 mesmo ? Caramba… :shock:

[quote=Paulo Silveira] <property name="hibernate.c3p0.max_size">20</property> [/quote]

[quote=boaglio]
É só 20 mesmo ? Caramba… :shock:

[quote=Paulo Silveira] <property name="hibernate.c3p0.max_size">20</property> [/quote][/quote]

como assim só ??

20 conexões concorrentes, é bastante… quase nunca é preciso de tantas

o primeiro replay do paulo ele explicou o problema

[quote=Paulo Silveira]Ola Lavieri!..
Primeiro, sobre o GUJ, achei estranho, pq usamos o c3p0. E adivinha? Com a atulizacao do JForum as variaveis estao comentadas!!! Vamos mudar e no proximo restart …/quote]

[quote=Lavieri]
20 conexões concorrentes, é bastante… quase nunca é preciso de tantas[/quote]

Exato! Aposto que 5 conexoes dariam conta.

Imagine que nossa transacao demora 2 segundos. Podemos ter 20 usuarios “simultaneos” numa janela de 2 segundos (isso daria 800 mil transacoes por dia mais ou menos, eh MUITA coisa). se aparecesse um 21o, ele teria de esperar so 2 segundos, no pior dos casos.