Я разрабатываю приложение, которое подключается к слоту логической репликации для использования событий WAL. Затем эти события WAL пересылаются брокеру MQ. Это прекрасно работает, но я заметил, что через некоторое время мне не хватает памяти. Мне удалось минимизировать проблему до кода, отвечающего за получение событий WAL. Это происходит со следующим кодом:
final Properties properties = new Properties();
PGProperty.USER.set(properties, "user");
PGProperty.PASSWORD.set(properties, "password");
PGProperty.ASSUME_MIN_SERVER_VERSION.set(properties, "9.4");
PGProperty.REPLICATION.set(properties, "database");
PGProperty.PREFER_QUERY_MODE.set(properties, "simple");
while (true) {
Connection connection = null;
PGConnection PGConnection = null;
PGReplicationStream stream = null;
try {
connection = DriverManager.getConnection("jdbc:postgresql://localhost:5432/db", properties);
PGConnection = connection.unwrap(PGConnection.class);
stream = PGConnection.getReplicationAPI().replicationStream().logical().withSlotName("slot").start();
while (true) {
final ByteBuffer buffer = stream.read();
// ... logic here ... (disabled during memory test)
stream.setAppliedLSN(stream.getLastReceiveLSN());
stream.setFlushedLSN(stream.getLastReceiveLSN());
}
} catch (final SQLException e1) {
Logger.getLogger(getClass()).error(e1);
if (stream != null) {
try {
stream.close();
} catch (final SQLException e2) {
Logger.getLogger(getClass()).error(e2);
}
}
if (connection != null) {
try {
connection.close();
} catch (final SQLException e2) {
Logger.getLogger(getClass()).error(e2);
}
}
}
}
Я закомментировал логику разбора сообщения и пересылки его MQ-брокеру, так как нехватка памяти происходит и без этого.
Я также попытался изменить этот пример, используя метод опроса readPending()
вместо метода блокировки read()
(как показано в https://jdbc.postgresql.org/documentation/head/replication.html), но проблема остается.
Я также заметил, что через некоторое время приложение загружает процессор на 100%. Это должно быть вызвано базовыми библиотеками, так как read()
в этот момент все еще нормально обрабатывается (то есть последовательно обрабатывает каждое событие WAL).
Во время этих тестов я выполняю запросы INSERT
и UPDATE
с низкой скоростью.
Я использую следующую зависимость:
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.1.4</version>
</dependency>
Приложение работает как WAR в контейнере Tomcat8.
Есть идеи, что происходит?
обновление 1
Я понял, что происходит, но пока не могу объяснить. Я расскажу подробности.
Каждые 10 секунд я делаю запросы INSERT
и UPDATE
, как сказано. Эти запросы приводят к 645 событиям WAL. Итак, каждые 10 секунд мне приходится read()
645 событий. Вначале это занимает 0 (иногда 1) миллисекунд до read()
одного события. Через некоторое время это занимает 1 миллисекунду. Затем, снова через некоторое время, это занимает 2 миллисекунды. И так далее...
Итак, через некоторое время я не могу read()
645 событий за 10 секунд, так как время, необходимое для read()
, продолжает увеличиваться. Это объясняет 100% загрузку ЦП и нехватку памяти.
Я до сих пор не знаю, как это объяснить и как это исправить. Я продолжу расследование.
обновление 2
Я пытался добавить buffer.clear()
в конце цикла, но безуспешно. Я все еще сталкиваюсь со 100% проблемами с процессором и памятью. Это ожидаемо, потому что буфер — это локальная переменная, поэтому он в любом случае обрабатывается сборщиком мусора после каждого цикла. Но я подумал, что было бы неплохо проверить в любом случае.