#4393·hertzbeat

[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/streamConversationServiceImpl.streamChat)在调用 messageDao.findByConversationIdOrderByGmtCreateAsc(...) 时失败,调用链:ChatController.streamChatConversationServiceImpl.streamChat:95$Proxy280.findByConversationIdOrderByGmtCreateAsc → Hibernate ClobJdbcType.doExtractDataHelper.extractStringPgClob.getCharacterStreamAbstractBlobClob.getLoLargeObjectManager.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

  1. 启动使用 PostgreSQL 17 作为元数据库的 HertzBeat(spring.profiles.active=proddriver-class-name=org.postgresql.Driverjpa.hibernate.ddl-auto=updateflyway.enabled=false)。
  2. 确认 ChatMessage 实体(hzb_ai_message 表)的 content 列在 PostgreSQL 上以 OID 形式存在:
    sql
    \d hzb_ai_message
    -- Column  |       Type        | ...
    -- content | oid               |
    这是由 ChatMessage.content 上的 @Lob + String 在 Hibernate ddl-auto=update 下的默认映射(PostgreSQL 上 @Lob Stringoid)。
  3. 通过已认证用户调用 AI 对话接口,先 POST /api/chat/conversations 创建会话,再 POST /api/chat/stream 发送一条消息:
    bash
    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}"
  4. 服务端 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)
  5. 再调用 GET /api/chat/conversations/$CONV_ID 同样复现异常,调用栈指向 ConversationServiceImpl.getConversation:187

Environment

markdown
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

log
[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(commit 307ef0d49),相关 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(commit aac5bafe4,修复 LazyInitializationException,未触及 @Lob
    • #4208 fix(ai): correct conversation message handling(commit aee4fcc19,修复 conversationId insertable 标志错误,未触及 @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
  • 建议的修复方向(仅作参考,非强制):
    1. ChatMessage.content@Lob 改为 @Column(columnDefinition = "TEXT", nullable = false),让 Hibernate 在 PostgreSQL 上映射为 text 而非 oid,绕过 Large Object 路径。
    2. ConversationServiceImpl.getConversationgetAllConversations@Transactional(readOnly = true)streamChat 不加,因为返回 Flux,事务会阻塞 SSE 流式推送并占用 Hikari 连接池)。
    3. 由于 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.yamlhertzbeat 服务,profiles.active=prodflyway.enabled=false