Чтение большого двоичного объекта PostgreSQL с использованием JDBC в нескольких транзакциях: «ОШИБКА: недопустимый дескриптор большого объекта: 0»

#java #postgresql #jdbc #blob #pgbouncer

Вопрос:

У меня есть база данных, в которой хранятся большие большие двоичные объекты, некоторые из них >1 ГБ, за pg_bouncer с объединением транзакций. Я часто получаю ошибки query_wait_timeout от pg_bouncer, и я обнаружил, что основной причиной является время, необходимое для чтения больших двоичных объектов. Просто все соединения заняты чтением больших двоичных объектов, что занимает значительно больше времени, чем query_wait_timeout, поэтому другие транзакции тем временем остаются в ожидании.

Я придумал это решение для чтения по частям, чтобы дать другим (более коротким) транзакциям возможность выполнить и возобновить чтение большого двоичного объекта позже. Код действительно прост:

 final ByteArrayOutputStream baos = new ByteArrayOutputStream();
do {
    tx = beginTransaction();
    chunk = blob.getBytes(pos, blobReadChunkLength);
    if (chunk.length > 0)
        baos.write(chunk);
    tx.commit();
    pos  = chunk.length;
} while (chunk.length > 0);
 

(Да, в середине есть Hibernate, но Hibernate просто возвращает экземпляр большого двоичного объекта JDBC, так что это похоже на то, как если бы его там не было).

Проблема в том, что если описанный выше цикл выполняется более одного раза, так что транзакция фиксируется (или откатывается) и открывается новая, то getBytes() возникает исключение:

 Caused by: org.postgresql.util.PSQLException: ERROR: invalid large-object descriptor: 0
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2510)
    at org.postgresql.core.v3.QueryExecutorImpl.receiveFastpathResult(QueryExecutorImpl.java:805)
    at org.postgresql.core.v3.QueryExecutorImpl.fastpathCall(QueryExecutorImpl.java:592)
    at org.postgresql.fastpath.Fastpath.fastpath(Fastpath.java:105)
    at org.postgresql.fastpath.Fastpath.fastpath(Fastpath.java:146)
    at org.postgresql.largeobject.LargeObject.seek(LargeObject.java:258)
    at org.postgresql.jdbc.AbstractBlobClob.getBytes(AbstractBlobClob.java:108)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.hibernate.engine.jdbc.SerializableBlobProxy.invoke(SerializableBlobProxy.java:72)
    at com.sun.proxy.$Proxy27.getBytes(Unknown Source)
 

Это исключение не возникает, если большой двоичный объект достаточно мал, чтобы его можно было прочитать только в одном фрагменте (или если blobReadChunkLength он достаточно велик, но это упускает смысл этой реализации).

Я хотел бы знать. как правильно это сделать? Вызов функции free() для большого двоичного объекта перед совершением транзакции только ухудшил ситуацию.

Действительно ли невозможно использовать один и тот же экземпляр большого двоичного объекта в нескольких транзакциях?

В качестве альтернативы, я думаю, что мог бы легко реализовать это поверх функций PostgreSQL, таких как lo_read (), за счет переносимости. Но для этого мне нужно получить деньги. Можно ли получить loid в JDBC или в режиме гибернации?

Комментарии:

1. Идентификатор OID большого объекта-это значение, хранящееся в вашем «столбце больших двоичных объектов», которое определяется как oid

2. @a_horse_with_no_name да, это в столбце, но на стороне java я получаю только экземпляр java.sql.Blob, а не oid в качестве int

3. Вы должны быть в состоянии получить этот номер через ResultSet.getLong()

4. Hibernate не предоставляет набор результатов, только экземпляр java.sql.Blob. Удаление этого промежуточного слоя означало бы рефакторинг всего приложения :-/

5. Радости слоев запутывания… Что произойдет, если вы просто используете getLong() все, что дает вам Hibernate? Или просто сопоставьте столбец в режиме гибернации как Long , а затем используйте встроенную функцию для дальнейшего доступа.