Основной причиной в вашем случае является ошибка в 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
TcpRemoteBufferSize
вfirebird.conf
? Не могли бы вы также опубликовать дамп застрявшей темы? Какой уровень изоляции транзакций вы используете? В качестве обходного пути вы можете настроить свойство подключенияsoTimeout
, чтобы установить время ожидания чтения сокета (см. также Руководство по Jaybird), но такие зависания указывают либо на то, что Firebird блокируется при чтении, либо на проблему в проводном протоколе. Было бы полезно иметь минимальный воспроизводимый пример. - person Mark Rotteveel   schedule 15.11.2019