Вопрос или проблема
Я создал простую программу, которая многократно устанавливает соединение с моей базой данных, а затем закрывает это соединение. Программа выдает ошибку на шестой или седьмой итерации. Если я ставлю программу на паузу между каждой итерацией на пять секунд, ошибка возникает на третьей итерации. Вывод статуса соединения показывает, что они закрываются успешно, поэтому утечка соединений не должна происходить. Мои соединения устанавливаются с помощью HikariCP и аутентификации IAM от Google Cloud.
package Tester;
import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import InstanceCreator.InstanceCreator;
public class Tester {
public static void main(String[] args) throws SQLException, InterruptedException {
int i = 1;
while (true) {
System.out.println("Начало цикла: " + i);
i++;
Thread.sleep(5000);
DataSource dataSource = InstanceCreator.createConnectionPool();
Connection connection = null;
try {
connection = dataSource.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
finally {
connection.close();
}
}
}
}
package InstanceCreator;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
public class InstanceCreator {
private static final String INSTANCE_CONNECTION_NAME = "mycreds";
private static final String DB_IAM_USER = "mycreds";
private static final String DB_NAME = "mycreds";
public static DataSource createConnectionPool() {
HikariConfig config = new HikariConfig();
config.setJdbcUrl(String.format("jdbc:postgresql:///%s", DB_NAME));
config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory");
config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME);
config.addDataSourceProperty("enableIamAuth", "true");
config.addDataSourceProperty("user", DB_IAM_USER);
config.addDataSourceProperty("password", "password");
config.addDataSourceProperty("sslmode", "disable");
return new HikariDataSource(config);
}
}
Начало цикла: 1
SLF4J(W): Не найдено никаких провайдеров SLF4J.
SLF4J(W): Установлено значение по умолчанию для реализации логирования (NOP)
SLF4J(W): Для получения дополнительных сведений см. https://www.slf4j.org/codes.html#noProviders.
Начало цикла: 2
Начало цикла: 3
Начало цикла: 4
Начало цикла: 5
Начало цикла: 6
Начало цикла: 7
Исключение в потоке "main" com.zaxxer.hikari.pool.HikariPool$PoolInitializationException: Не удалось инициализировать пул: FATAL: оставшиеся слоты соединений зарезервированы для суперпользовательских соединений, не связанных с репликацией
в com.zaxxer.hikari.pool.HikariPool.throwPoolInitializationException(HikariPool.java:596)
в com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:582)
в com.zaxxer.hikari.pool.HikariPool.<init>(HikariPool.java:115)
в com.zaxxer.hikari.HikariDataSource.<init>(HikariDataSource.java:81)
в InstanceCreator.InstanceCreator.createConnectionPool(InstanceCreator.java:27)
в Tester.Tester.main(Tester.java:17)
Причина: org.postgresql.util.PSQLException: FATAL: оставшиеся слоты соединений зарезервированы для суперпользовательских соединений, не связанных с репликацией
в org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2676)
в org.postgresql.core.v3.QueryExecutorImpl.readStartupMessages(QueryExecutorImpl.java:2788)
в org.postgresql.core.v3.QueryExecutorImpl.<init>(QueryExecutorImpl.java:174)
в org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:313)
в org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:54)
в org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:253)
в org.postgresql.Driver.makeConnection(Driver.java:434)
в org.postgresql.Driver.connect(Driver.java:291)
в com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:121)
в com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:364)
в com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:206)
в com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:476)
в com.zaxxer.hikari.pool.HikariPool.checkFailFast(HikariPool.java:561)
... 4 more
Ответ или решение
Ошибка "FATAL: remaining connection slots are reserved for non-replication superuser connections" указывает, что количество доступных соединений с базой данных перевалило за установленный предел. Ваша программа, как видно из предоставленного кода, создает новый пул соединений при каждой итерации цикла, что приводит к тому, что база данных не успевает освободить соединения, даже если вы их закрываете.
Причины проблемы
-
Создание нового пула на каждой итерации. Каждый раз при вызове
createConnectionPool()
создается новый экземплярHikariDataSource
, и, следовательно, новый пул соединений. Это означает, что до тех пор, пока не будут закрыты все соединения из предыдущего пула, вы можете истощить доступные соединения. -
Неправильное закрытие соединений. Ваш код закрывает соединения в блоке
finally
, но еслиgetConnection()
выбрасывает исключение, то вы пытаетесь закрыть соединение, которое на самом деле не было открыто. Это может вызвать дополнительные проблемы. -
Конфигурация соединений. Убедитесь, что в вашей базе данных ограничение на соединения (параметр
max_connections
) адекватно отвечает вашим требованиям.
Решение проблемы
Вот несколько шагов, которые помогут вам устранить проблему:
- Используйте один пул соединений. Вместо создания нового пула соединений в каждой итерации, создайте его один раз вне цикла. Вы можете сделать это, переместив создание пула в статическую переменную или использовать Singleton.
public class Tester {
private static DataSource dataSource = InstanceCreator.createConnectionPool();
public static void main(String[] args) throws SQLException, InterruptedException {
int i = 1;
while (true) {
System.out.println("Starting loop: " + i);
i++;
Thread.sleep(5000);
try (Connection connection = dataSource.getConnection()) {
// Используйте соединение здесь
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
- Проверьте количество доступных соединений. Убедитесь, что количество, определенное в базе данных и в HikariCP, соответствует вашим потребностям. Вы можете настроить максимальное количество соединений в вашем пуле HikariCP, используя движки, такие как:
config.setMaximumPoolSize(10); // или любое другое подходящее значение
-
Настройте max_connections в PostgreSQL. Если ваш случай использования требует больше соединений, чем текущее ограничение, рассмотрите возможность увеличения
max_connections
в вашей базе данных. Однако будьте осторожны — слишком большое количество соединений может негативно сказаться на производительности. -
Регулярно проверяйте состояние соединений. Вы можете настроить HikariCP для периодической проверки состояния соединений, чтобы удостовериться, что закрытые соединения действительно освобождаются.
Следуя этим рекомендациям, вы должны устранить проблему с превышением лимита соединений и обеспечить более стабильную работу вашей программы.