Блокировка потока Jaybird SocketInputStream.socketread0

Я использую соединение JDBC с firebird.

<groupId>org.firebirdsql.jdbc</groupId>
<artifactId>jaybird-jdk18</artifactId>
<version>3.0.5</version>

Когда я читаю данные с помощью:

resultSet.next(); //resultSet is instance of FBResultSet

иногда поток блокируется здесь:

 private native int socketRead0(FileDescriptor fd,
                                   byte b[], int off, int len,
                                   int timeout)
        throws IOException;

И нить никогда не продолжится. Мне нужно перезапустить все приложение. :(

Как я могу решить эту проблему? Я попробовал некоторые параметры отсюда: https://dzone.com/articles/threads-stuck-in-javanetsocketinputstreamsocketrea

-Dsun.net.client.defaultConnectTimeout=10000
-Dsun.net.client.defaultReadTimeout=10000

Но без положительного результата. Я даже не знаю, каково значение тайм-аута. Я не могу отладить его в этом месте. (java.net.SocketInputStream)

Соединение с базой данных связано с облачным сервером, и я думаю, что проблема в этом.

Можно ли как-то исправить эту проблему? Или настроить тайм-аут для jdbc firebird?

Спасибо за любой ответ.

Редактировать: вот экран застрявшей ветки: введите здесь описание изображения


person Altair    schedule 15.11.2019    source источник
comment
Вам нужно посмотреть конфигурацию JDBC, чтобы решить эту проблему.   -  person user207421    schedule 15.11.2019
comment
Используете ли вы пользовательское значение для TcpRemoteBufferSize в firebird.conf? Не могли бы вы также опубликовать дамп застрявшей темы? Какой уровень изоляции транзакций вы используете? В качестве обходного пути вы можете настроить свойство подключения soTimeout, чтобы установить время ожидания чтения сокета (см. также Руководство по Jaybird), но такие зависания указывают либо на то, что Firebird блокируется при чтении, либо на проблему в проводном протоколе. Было бы полезно иметь минимальный воспроизводимый пример.   -  person Mark Rotteveel    schedule 15.11.2019
comment
поэтому таймаут помог. Большое спасибо. :)   -  person Altair    schedule 15.11.2019
comment
Я не использую TcpRemoteBufferSize, не так ли?   -  person Altair    schedule 15.11.2019
comment
@Altair Нет, при тестировании Jaybird я наблюдал проблемы с зависанием чтения в Firebird 3 и 4, если используется проводное шифрование (по умолчанию), а для TcpRemoteBufferSize в firebird.conf установлены значения, не кратные 4 (или, может быть, 8). ). Однако было бы хорошо знать, является ли это ошибкой в ​​Firebird или Jaybird, или это просто вопрос блокировки Firebird в ожидании блокировки, поэтому минимальный воспроизводимый пример и трассировка стека зависания были бы полезны. Вы также можете отправить его в список рассылки firebird-java или опубликовать на tracker.firebirdsql.org/browse/JDBC.   -  person Mark Rotteveel    schedule 15.11.2019
comment
Я постараюсь сделать минимально воспроизводимый пример. :) Спасибо.   -  person Altair    schedule 15.11.2019


Ответы (1)


Основной причиной в вашем случае является ошибка в Jaybird 3.0.4–3.0.7 (и 4.0.0-beta-1) с Firebird 3 (и 4), когда в firebird.conf включено шифрование проводов (настройка WireCrypt = Enabled).

Раньше я видел эту проблему только с Jaybird 4, когда тесты Jaybird запускались в определенном порядке, и, поскольку эта версия еще не выпущена, я не придавал ей приоритета для поиска основной причины.

С вашей помощью (еще раз спасибо!) я смог определить проблему. Эта ошибка теперь исправлена ​​в Jaybird 3.0.8, который доступен по адресу Firebird: драйвер JDBC.

Объяснение ошибки

Некоторые типы буферов (например, данные столбца) в проводном протоколе Firebird дополняются байтами, кратными четырем. Реализация в Jaybird полагалась на InputStream.skip(long), чтобы пропустить это заполнение.

В частности, было сделано:

public int skipFully(int n) throws IOException {
    int total = 0;
    int cur;
    while (total < n && (cur = (int) in.skip(n - total)) > 0) {
        total += cur;
    }
    return total;
}

Это прекрасно работало без проводного шифрования, потому что комбинация реализаций skip в BufferedInputStream и SocketInputStream пропускала как минимум 1 байт при каждом вызове skip, если только сокет не был закрыт.

Когда был добавлен CipherInputStream, это ожидание больше не выполнялось: если BufferedInputStream не имеет байтов в своем буфере, он вызовет skip(long) для CipherInputStream, а если он находится в конце его буфера, он пропустит 0 байтов (что разрешено для skip).

В результате Jaybird мог пропустить на 1, 2 или 3 байта меньше, чем нужно, что приводило к тому, что последующие чтения считывали неправильные данные. В конечном итоге это либо приведет к неправильному считыванию размера буфера, что приведет к блокировке чтения в ожидании дополнительных данных, либо к тому, что Jaybird прочитает неправильный код операции, что вызовет исключение с сообщением, начинающимся с "Unsupported или непредвиденный код операции".

Обходные пути или решения для других проблем с аналогичным поведением

В качестве обходного пути для зависших соединений можно использовать свойство соединения soTimeout, чтобы установить время ожидания чтения сокета (см. Руководство Jaybird, приложение A.2 Другие свойства).

Проблема также может заключаться в том, что Firebird «просто» бесконечно ждет блокировки. Чтобы исключить ожидание Firebird блокировки, вы также можете изменить режим ожидания транзакции или время ожидания. По умолчанию для Jaybird используется неопределенное ожидание.

Например, чтобы изменить режим ожидания чтения, зафиксированного на отсутствие ожидания, вы можете установить свойство соединения TRANSACTION_READ_COMMITTED со значением read_committed,rec_version,write,nowait.

Чтобы использовать ожидание, но с тайм-аутом, вы можете использовать значение read_committed,rec_version,write,wait,lock_timeout=5. Это применит 5-секундный тайм-аут блокировки.

Если ваша проблема связана с тем, что Firebird ожидает блокировку, удерживаемую другой транзакцией, это должно привести к исключению с текстом, содержащим конфликт блокировок при транзакции без ожидания (для параметра nowait) или тайм-аут блокировки ожидания транзакции (для параметра lock_timeout).

См. также Руководство Jaybird, приложение A.3 Уровни изоляции транзакций и Руководство Jaybird, раздел 6.4. Уровни изоляции транзакций (хотя теперь я вижу, что мне нужно будет добавить туда больше информации).

person Mark Rotteveel    schedule 15.11.2019