Postgres/JDBC/логическая репликация — проблемы с нехваткой памяти

Я разрабатываю приложение, которое подключается к слоту логической репликации для использования событий 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% проблемами с процессором и памятью. Это ожидаемо, потому что буфер — это локальная переменная, поэтому он в любом случае обрабатывается сборщиком мусора после каждого цикла. Но я подумал, что было бы неплохо проверить в любом случае.


person YasonTR    schedule 15.09.2017    source источник


Ответы (1)


Я нашел причину, по которой мне не хватало памяти. Я тестировал с помощью подключаемого модуля вывода декодирования decoderbufs, https://github.com/xstevens/decoderbufs. При замене встроенным плагином test или wal2json (https://github.com/eulerto/wal2json), у меня таких проблем нет.

Я постараюсь сообщить автору плагина decoderbufs.

person YasonTR    schedule 15.09.2017