[BUG] PostgreSQL 上 AI 对话历史查询抛出 Unable to access lob stream
Author: chenxu8989Created Sep 21, 2026Updated Sep 21, 2026
Labelsbug
Is there an existing issue for this?
- I have searched the existing issues
Current Behavior
在 PostgreSQL 部署环境下调用 AI 对话相关接口(HertzBeat v1.9.0 自定义镜像,Spring profile=prod,Hibernate ddl-auto=update)会抛出 JpaSystemException: Unable to access lob stream,根因是 org.postgresql.util.PSQLException: 大型对象无法被使用在自动确认事物交易模式。
具体表现:
POST /api/chat/stream(ConversationServiceImpl.streamChat)在调用messageDao.findByConversationIdOrderByGmtCreateAsc(...)时失败,调用链:ChatController.streamChat→ConversationServiceImpl.streamChat:95→$Proxy280.findByConversationIdOrderByGmtCreateAsc→ HibernateClobJdbcType.doExtract→DataHelper.extractString→PgClob.getCharacterStream→AbstractBlobClob.getLo→LargeObjectManager.open。GET /api/chat/conversations/{id}(ConversationServiceImpl.getConversation:187)触发同样的异常。- 异常会被
org.apache.hertzbeat.manager.support.GlobalExceptionHandler捕获并打印[database error happen]-Unable to access lob stream。
Expected Behavior
- AI 对话流式接口
/api/chat/stream能正常返回 SSE 流式响应,不再抛 LOB 异常。 /api/chat/conversations与/api/chat/conversations/{id}正常返回对话历史与消息列表。- 在 PostgreSQL 部署(
org.postgresql.Driver)下不依赖事务包裹即可正确读取消息内容字段。
Steps To Reproduce
- 启动使用 PostgreSQL 17 作为元数据库的 HertzBeat(
spring.profiles.active=prod,driver-class-name=org.postgresql.Driver,jpa.hibernate.ddl-auto=update,flyway.enabled=false)。 - 确认
ChatMessage实体(hzb_ai_message表)的content列在 PostgreSQL 上以 OID 形式存在:这是由\d hzb_ai_message -- Column | Type | ... -- content | oid |ChatMessage.content上的@Lob+String在 Hibernate ddl-auto=update 下的默认映射(PostgreSQL 上@Lob String→oid)。 - 通过已认证用户调用 AI 对话接口,先 POST
/api/chat/conversations创建会话,再 POST/api/chat/stream发送一条消息:CONV_ID=$(curl -s -X POST http://localhost:1157/api/chat/conversations \ -H 'Authorization: ...' | jq -r '.data.id') curl -N -X POST http://localhost:1157/api/chat/stream \ -H 'Content-Type: application/json' \ -H 'Authorization: ...' \ -d "{\"message\":\"hello\",\"conversationId\":$CONV_ID}" - 服务端
hertzbeat容器日志抛出:org.postgresql.util.PSQLException: 大型对象无法被使用在自动确认事物交易模式。 at org.postgresql.largeobject.LargeObjectManager.open(LargeObjectManager.java:243) at org.postgresql.jdbc.AbstractBlobClob.getLo(AbstractBlobClob.java:272) at org.postgresql.jdbc.PgClob.getCharacterStream(PgClob.java:54) at org.hibernate.type.descriptor.java.DataHelper.extractString(DataHelper.java:254) - 再调用
GET /api/chat/conversations/$CONV_ID同样复现异常,调用栈指向ConversationServiceImpl.getConversation:187。
Environment
HertzBeat version(s): hertzbeat 1.9.0
- Spring profile: `prod`
- 数据库: PostgreSQL 17(容器镜像 `pgvector/pgvector:pg17`)
- JDBC: `jdbc:postgresql://postgres:5432/hertzbeat`
- JPA / Hibernate: `ddl-auto=update`,`Flyway=enabled:false`Debug logs
[hertzbeat] | org.springframework.orm.jpa.JpaSystemException: Unable to access lob stream
[hertzbeat] | at org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator.convertHibernateAccessException(HibernateExceptionTranslator.java:223)
[hertzbeat] | at org.springframework.orm.jpa.hibernate.HibernateExceptionTranslator.convertHibernateAccessException(HibernateExceptionTranslator.java:131)
[hertzbeat] | ...
[hertzbeat] | at jdk.proxy2/jdk.proxy2.$Proxy280.findByConversationIdOrderByGmtCreateAsc(Unknown Source)
[hertzbeat] | at org.apache.hertzbeat.ai.service.impl.ConversationServiceImpl.streamChat(ConversationServiceImpl.java:95)
[hertzbeat] | ...
[hertzbeat] | at org.apache.hertzbeat.ai.controller.ChatController.streamChat(ChatController.java:86)
[hertzbeat] | ...
[hertzbeat] | Caused by: org.hibernate.HibernateException: Unable to access lob stream
[hertzbeat] | at org.hibernate.type.descriptor.java.DataHelper.extractString(DataHelper.java:261)
[hertzbeat] | at org.hibernate.type.descriptor.java.StringJavaType.wrap(StringJavaType.java:125)
[hertzbeat] | at org.hibernate.type.descriptor.java.StringJavaType.wrap(StringJavaType.java:26)
[hertzbeat] | at org.hibernate.type.descriptor.jdbc.ClobJdbcType$1.doExtract(ClobJdbcType.java:55)
[hertzbeat] | ...
[hertzbeat] | Caused by: org.postgresql.util.PSQLException: 大型对象无法被使用在自动确认事物交易模式。
[hertzbeat] | at org.postgresql.largeobject.LargeObjectManager.open(LargeObjectManager.java:243)
[hertzbeat] | at org.postgresql.jdbc.AbstractBlobClob.getLo(AbstractBlobClob.java:272)
[hertzbeat] | at org.postgresql.jdbc.PgClob.getCharacterStream(PgClob.java:54)
[hertzbeat] | at org.hibernate.type.descriptor.java.DataHelper.extractString(DataHelper.java:254)
[hertzbeat] | 2026-09-21 10:57:54 [http-nio-1157-exec-7] WARN org.apache.hertzbeat.manager.support.GlobalExceptionHandler - [database error happen]-Unable to access lob stream
[hertzbeat] | org.springframework.orm.jpa.JpaSystemException: Unable to access lob stream
[hertzbeat] | ...
[hertzbeat] | at jdk.proxy2/jdk.proxy2.$Proxy280.findByConversationIdOrderByGmtCreateAsc(Unknown Source)
[hertzbeat] | at org.apache.hertzbeat.ai.service.impl.ConversationServiceImpl.getConversation(ConversationServiceImpl.java:187)附注:
- 在开发默认的 H2 数据库上无法复现,因为 H2 上
@Lob String映射为CLOB/VARCHAR,不走 PostgreSQL 的 Large Object 路径。 - 上游 Apache HertzBeat
hertzbeat-common-spring/src/main/java/org/apache/hertzbeat/common/entity/ai/ChatMessage.java:75仍保留@Lob注解。截至当前 master(commit307ef0d49),相关 AI 修复(#3911、#4208、#4280、#4315、#4317)均未触及@Lob。 - 期望根因:实体字段声明
@Lob + String在 PostgreSQL 上被 Hibernate 映射为 OID(Large Object)。读取 Large Object 必须处于显式事务中,但streamChat/getConversation调用messageDao.findByConversationIdOrderByGmtCreateAsc时不在事务上下文,PG JDBC 驱动拒绝读取,抛出大型对象无法被使用在自动确认事物交易模式。
Anything else?
- 相关 Issue / PR(已检索,均未解决此问题):
- #3911
[bugfix]: AI conversation message loading issue(commitaac5bafe4,修复 LazyInitializationException,未触及@Lob) - #4208
fix(ai): correct conversation message handling(commitaee4fcc19,修复conversationIdinsertable 标志错误,未触及@Lob) - #4280
maintenance: scope AI conversations by creator(多租户隔离,未触及@Lob) - #4315
fix(ai): create conversation when conversation ID is missing(自动建会话,未触及@Lob) - #4317
fix(ai): persist parameters for scheduled skills(调度任务参数,未触及@Lob)
- #3911
- 建议的修复方向(仅作参考,非强制):
- 将
ChatMessage.content的@Lob改为@Column(columnDefinition = "TEXT", nullable = false),让 Hibernate 在 PostgreSQL 上映射为text而非oid,绕过 Large Object 路径。 - 给
ConversationServiceImpl.getConversation与getAllConversations加@Transactional(readOnly = true)(streamChat不加,因为返回Flux,事务会阻塞 SSE 流式推送并占用 Hikari 连接池)。 - 由于
ddl-auto=update不会自动把oid列降级为text,需要在 PG 上手工执行一次ALTER TABLE hzb_ai_message ALTER COLUMN content TYPE TEXT USING content::text;(OID 是 PG 中的数字,可直接 cast 为 text)。
- 将
- 自定义镜像
hertzbeat-ext的部署栈:/home/chenxu/project/custom-monitoring/docker-stack/docker-compose.yaml中hertzbeat服务,profiles.active=prod,flyway.enabled=false。
Source: apache/hertzbeat