JPA - java.net.SocketException: Broken pipe

Estou fazendo um site usando JPA, mas um problema que estou tendo é que, quando coloco o site no ar, depois de algumas horas, algum problema acontece na conexão com o banco, dá algum problema numa transaction, que fica lançando exceção.

Para voltar ao normal eu tenho que reiniciar o Tomcat.

A exceção é essa:

java.net.SocketException: Broken pipe
java.net.SocketOutputStream.socketWrite0(Native Method)
java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:92)
java.net.SocketOutputStream.write(SocketOutputStream.java:136)
java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
org.postgresql.core.PGStream.flush(PGStream.java:532)
org.postgresql.core.v3.QueryExecutorImpl.sendSync(QueryExecutorImpl.java:674)
org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:189)
org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:452)
org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:354)
org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:258)
org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
org.hibernate.loader.Loader.getResultSet(Loader.java:1787)
org.hibernate.loader.Loader.doQuery(Loader.java:674)
org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
org.hibernate.loader.Loader.doList(Loader.java:2213)
org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
org.hibernate.loader.Loader.list(Loader.java:2099)
org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:289)
org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1695)
org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:142)
org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:152)
org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:65)
br.com.meusite.service.UsuarioService.add(UsuarioService.java:290)

Estou usando somente um mesmo EntityManager para a aplicação inteira (Singleton), portanto, quando dá um erro, eu já não consigo entrar em nenhuma página do site.

Se eu ficar dando um “entityManager.close();” no bloco finally de cada método service acho que resolve o problema, mas aí eu irei perder o cache que o EntityManager gera automaticamente para mim.

Alguém tem alguma sugestão para resolver o problema?

Olá, como você está estabelendo a conexão com o banco ?

Estou usando JPA.

Meu persistence.xml está assim:

<persistence>
    <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.ejb.HibernatePersistence</provider>
        <properties>
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />
            <property name="transaction.factory_class" value="org.hibernate.transaction.JDBCTransactionFactory" />
            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
            
            <property name="hibernate.connection.username" value="username"/>
            <property name="hibernate.connection.password" value="password"/>
            <property name="hibernate.connection.url" value="jdbc:postgresql://pgsql.server.com.br:5432/site"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="false"/>
            
        </properties>
    </persistence-unit>
</persistence>

Criei uma classe BasicService, que toda classe service extende:

//imports...
public class BasicService {

    protected static EntityManagerFactory entityManagerFactory;
    protected static EntityManager entityManager;
    protected static EntityTransaction transaction;
    
    static {
        entityManagerFactory = Persistence.createEntityManagerFactory("persistenceUnit");
        entityManager = entityManagerFactory.createEntityManager();
        transaction = entityManager.getTransaction();
    }
}

E aqui está um exemplo de uma classe service:

//imports...
public class UsuarioService extends BasicService {

    public void add(Usuario usuario) throws Exception {

        try {
            transaction.begin();
            entityManager.persist(usuario);
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            throw e;
        }

    }
    
    public void update(Usuario usuario) throws Exception {

        try {
            transaction.begin();
            entityManager.merge(usuario);
            transaction.commit();
        } catch (Exception e) {
            transaction.rollback();
            throw e;
        }

    }
}

Só isso. Sempre funcionou no ambiente de desenvolvimento. Na hora que foi para produção, depois de algumas horas no ar, dá algum problema numa transação que trava tudo, quando tenta dar transaction.begin(). Daí eu tenho que ficar reiniciando o Tomcat sempre quando dá aquela exceção.

Acho que primeiro dá uma exceção de Transaction Already Active, no transaction.begin(). Depois começa a dar exceção de Broken Pipe.

cara, muito estranho. aparantemente está tudo ok.
o banco nao está caindo ? verifica o uptime do banco quando ocorre esse erro. se estiver sua aplicacao não pode garantir isso.
ou até mesmo uma instabilidade da rede.
nesse caso, você pode configurar um pool para recriar as conexões quando fecharem.

abraços!

Como faço para para configurar esse pool?

Dê uma olhada nesse link http://www.onjava.com/pub/a/onjava/2006/04/19/database-connection-pooling-with-tomcat.html?page=1

abraços!

Olhando os logs to Tomcat, vi que logo antes de dar as exceções de Broken Pipe, a primeira exceção que dá é:

SEVERE: Servlet.service() for servlet default threw exception
org.postgresql.util.PSQLException: FATAL: terminating connection due to administrator command
	at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:1575)
	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1324)
	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:190)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.execute(AbstractJdbc2Statement.java:452)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeWithFlags(AbstractJdbc2Statement.java:354)
	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeQuery(AbstractJdbc2Statement.java:258)
	at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:186)
	at org.hibernate.loader.Loader.getResultSet(Loader.java:1787)
	at org.hibernate.loader.Loader.doQuery(Loader.java:674)
	at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
	at org.hibernate.loader.Loader.doList(Loader.java:2213)
	at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2104)
	at org.hibernate.loader.Loader.list(Loader.java:2099)
	at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:378)
	at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:338)
	at org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:172)
	at org.hibernate.impl.SessionImpl.list(SessionImpl.java:1121)
	at org.hibernate.impl.QueryImpl.list(QueryImpl.java:79)
	at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:65)
	at br.com.meusite.service.UsuarioService.findUsuarioByLogin(UsuarioService.java:61)

Ou seja, parece que a conexão foi derrubada por algum motivo.

Agora eu configurei o JPA para usar o pool do c3p0, vamos ver se resolve… Já coloquei o site no ar, agora é esperar para ver se o site vai durar muito tempo sem cair a conexão…

O persistence.xml ficou assim:

<persistence>   
    <persistence-unit name="persistenceUnit" transaction-type="RESOURCE_LOCAL">   
        <provider>org.hibernate.ejb.HibernatePersistence</provider>   
        <properties>   
            <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect" />   
            <property name="transaction.factory_class" value="org.hibernate.transaction.JDBCTransactionFactory" />   
            <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>   
               
            <property name="hibernate.connection.username" value="username"/>   
            <property name="hibernate.connection.password" value="password"/>   
            <property name="hibernate.connection.url" value="jdbc:postgresql://pgsql.server.com.br:5432/site"/>   
            <property name="hibernate.hbm2ddl.auto" value="update"/>   
            <property name="hibernate.show_sql" value="false"/>   
               
            <property name="hibernate.connection.provider_class" value="org.hibernate.connection.C3P0ConnectionProvider"/>
            <property name="hibernate.c3p0.acquire_increment" value="1"/>
            <property name="hibernate.c3p0.idle_test_period" value="100"/>
            <property name="hibernate.c3p0.max_size" value="100"/>
            <property name="hibernate.c3p0.max_statements" value="0"/>
            <property name="hibernate.c3p0.min_size" value="10"/>
            <property name="hibernate.c3p0.timeout" value="100"/>
        </properties>   
    </persistence-unit>   
</persistence>  

Depois de quebrar bastante a cabeça consegui resolver de 2 maneiras:

Talvez ajude se ainda já não conseguiu de outra forma…

[]'s