Apache Cayenne/PostgreSQL: ошибка слишком многих клиентов

Я использую Apache Cayenne 4 Milestone 5 и получаю эту ошибку. Приложение официально не развернуто, но тестируется в рамках IntelliJ IDEA.

Apr 03, 2017 10:19:58 PM org.postgresql.Driver connect
SEVERE: Connection error: 
org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
    at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:438)
    at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:222)
    at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
    at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:194)
    at org.postgresql.Driver.makeConnection(Driver.java:431)
    at org.postgresql.Driver.connect(Driver.java:247)
    at org.apache.cayenne.datasource.DriverDataSource.getConnection(DriverDataSource.java:159)
    at org.apache.cayenne.datasource.DriverDataSource.getConnection(DriverDataSource.java:133)
    at org.apache.cayenne.datasource.UnmanagedPoolingDataSource.createUnwrapped(UnmanagedPoolingDataSource.java:300)
    at org.apache.cayenne.datasource.UnmanagedPoolingDataSource.createWrapped(UnmanagedPoolingDataSource.java:293)
    at org.apache.cayenne.datasource.UnmanagedPoolingDataSource.createUnchecked(UnmanagedPoolingDataSource.java:273)
    at org.apache.cayenne.datasource.UnmanagedPoolingDataSource.<init>(UnmanagedPoolingDataSource.java:142)
    at org.apache.cayenne.datasource.PoolingDataSourceBuilder.buildPooling(PoolingDataSourceBuilder.java:92)
    at org.apache.cayenne.datasource.PoolingDataSourceBuilder.build(PoolingDataSourceBuilder.java:88)
    at org.apache.cayenne.configuration.server.XMLPoolingDataSourceFactory.getDataSource(XMLPoolingDataSourceFactory.java:75)
    at org.apache.cayenne.configuration.server.DelegatingDataSourceFactory.getDataSource(DelegatingDataSourceFactory.java:68)
    at org.apache.cayenne.configuration.server.DefaultDataNodeFactory.createDataNode(DefaultDataNodeFactory.java:79)
    at org.apache.cayenne.configuration.server.DataDomainProvider.addDataNode(DataDomainProvider.java:175)
    at org.apache.cayenne.configuration.server.DataDomainProvider.createAndInitDataDomain(DataDomainProvider.java:127)
    at org.apache.cayenne.configuration.server.DataDomainProvider.get(DataDomainProvider.java:91)
    at org.apache.cayenne.configuration.server.DataDomainProvider.get(DataDomainProvider.java:56)
    at org.apache.cayenne.di.spi.CustomProvidersProvider.get(CustomProvidersProvider.java:39)
    at org.apache.cayenne.di.spi.FieldInjectingProvider.get(FieldInjectingProvider.java:43)
    at org.apache.cayenne.di.spi.DefaultScopeProvider.get(DefaultScopeProvider.java:50)
    at org.apache.cayenne.di.spi.DefaultInjector.getInstance(DefaultInjector.java:139)
    at org.apache.cayenne.di.spi.FieldInjectingProvider.value(FieldInjectingProvider.java:105)
    at org.apache.cayenne.di.spi.FieldInjectingProvider.injectMember(FieldInjectingProvider.java:68)
    at org.apache.cayenne.di.spi.FieldInjectingProvider.injectMembers(FieldInjectingProvider.java:59)
    at org.apache.cayenne.di.spi.FieldInjectingProvider.get(FieldInjectingProvider.java:44)
    at org.apache.cayenne.di.spi.DefaultScopeProvider.get(DefaultScopeProvider.java:50)
    at org.apache.cayenne.di.spi.DefaultInjector.getInstance(DefaultInjector.java:134)
    at org.apache.cayenne.configuration.CayenneRuntime.newContext(CayenneRuntime.java:124)
    at com.company.databases.postgresql.accountsdb.dataservices.AccountServices.loginAsEmailAddress(AccountServices.java:454)
    at com.company.databases.postgresql.accountsdb.dataservices.AccountServices.login(AccountServices.java:445)
    at com.company.actions.fan.account.AccountActions.login(AccountActions.java:44)
    at com.company.serverapps.fans.entrypoints.standard.AccountConnections.login(AccountConnections.java:95)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)
    at org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)
    at org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:841)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1634)
    at org.eclipse.jetty.servlets.CrossOriginFilter.handle(CrossOriginFilter.java:308)
    at org.eclipse.jetty.servlets.CrossOriginFilter.doFilter(CrossOriginFilter.java:262)
    at org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1621)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:541)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1592)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:188)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1239)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:168)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:481)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1561)
    at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:166)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1141)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:213)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:132)
    at org.eclipse.jetty.server.Server.handle(Server.java:564)
    at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)
    at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)
    at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:279)
    at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:110)
    at org.eclipse.jetty.io.ChannelEndPoint$2.run(ChannelEndPoint.java:124)
    at org.eclipse.jetty.util.thread.Invocable.invokePreferred(Invocable.java:122)
    at org.eclipse.jetty.util.thread.strategy.ExecutingExecutionStrategy.invoke(ExecutingExecutionStrategy.java:58)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:201)
    at org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:133)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:672)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:590)
    at java.lang.Thread.run(Thread.java:745)

Apr 03, 2017 10:21:58 PM org.postgresql.Driver connect
SEVERE: Connection error: 
org.postgresql.util.PSQLException: FATAL: sorry, too many clients already
    at org.postgresql.core.v3.ConnectionFactoryImpl.doAuthentication(ConnectionFactoryImpl.java:438)
    at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:222)
    at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49)
    at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:194)
    at org.postgresql.Driver.makeConnection(Driver.java:431)
    at org.postgresql.Driver.connect(Driver.java:247)
    at org.apache.cayenne.datasource.DriverDataSource.getConnection(DriverDataSource.java:159)
    at org.apache.cayenne.datasource.DriverDataSource.getConnection(DriverDataSource.java:133)
    at org.apache.cayenne.datasource.UnmanagedPoolingDataSource.createUnwrapped(UnmanagedPoolingDataSource.java:300)
    at org.apache.cayenne.datasource.UnmanagedPoolingDataSource.createWrapped(UnmanagedPoolingDataSource.java:293)
    at org.apache.cayenne.datasource.UnmanagedPoolingDataSource.createUnchecked(UnmanagedPoolingDataSource.java:273)
    at org.apache.cayenne.datasource.UnmanagedPoolingDataSource.managePool(UnmanagedPoolingDataSource.java:188)
    at org.apache.cayenne.datasource.PoolingDataSourceManager.run(PoolingDataSourceManager.java:71)

Приложение работает нормально, но после некоторого использования показывает эту ошибку. В чем причина этой ошибки?

Я использую следующий метод для получения среды выполнения каждый раз, когда мне нужна транзакция.

public final static ServerRuntime getAccountsDBPostgreSQLRuntime() {
        return ServerRuntime.builder().addConfig("cayenne/cayenne-AccountsDB.project.xml").build();
    }

Пример:

public final static Long newFanAccount() {
        ServerRuntime accountsDBPostgresRuntime = AccountsDBRuntime.getAccountsDBPostgreSQLRuntime();
        ObjectContext context = accountsDBPostgresRuntime.newContext();
        Fan fan = context.newObject(Fan.class);
        context.commitChanges();
        Long fanID = (Long) Cayenne.pkForObject(fan);
        return fanID;
    }

person ikevin8me    schedule 03.04.2017    source источник
comment
Похоже, Apache Cayenne не использует пул соединений или допускает утечку соединений. Если вы не можете настроить его на использование меньшего количества соединений PostgreSQL, используйте внешний пул соединений, такой как pgBouncer.   -  person Laurenz Albe    schedule 03.04.2017
comment
Это немного странно, но мне не удалось найти, как вы освобождаете соединение с базой данных в Cayene. Ошибка ясно указывает на то, что происходит чрезмерное использование ресурсов, но вы не можете выразить, что вы закончили использовать этот конкретный ресурс (если только это не commitChanges()). Если это так, попробуйте обернуть все в блок try и rollback, если что-то поймаете.   -  person M. Prokhorov    schedule 03.04.2017
comment
@Laurenz, наверное, это имеет смысл, но я не уверен. Подождем еще ответов.   -  person ikevin8me    schedule 03.04.2017
comment
@Prokhorov Нет, commitChanges () не выдает никаких ошибок, и есть много успешных транзакций. Ошибки возникают после некоторого тестирования, например, более 30 успешных транзакций или около того. Думаю, подождем ответа эксперта по Cayenne. Они бы точно знали, почему и как предотвратить это.   -  person ikevin8me    schedule 03.04.2017


Ответы (1)


Из трассировки стека и примера кода видно, что вы не используете повторно стек Cayenne (объект ServerRuntime), вместо этого каждый раз создавая новый. Помимо того, что это медленно, это также приводит к утечке ресурсов. Каждый ServerRuntime содержит собственный пул соединений, который вы не отключаете. Так что рано или поздно сервер PostreSQL переполняется открытыми подключениями.

Решение состоит в том, чтобы превратить ServerRuntime в синглтон с областью действия приложения (например, сделать его статической переменной AccountsDBRuntime) и повторно использовать его всякий раз, когда вам нужен новый ObjectContext.

person andrus_a    schedule 03.04.2017