You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

触发PostgreSQL JDBC首次写入成功,后续失败问题求助

问题:DB2到PostgreSQL同步任务首次循环正常,后续写入失败

环境信息

  • IBM OS 7.4
  • PostgreSQL 13.7(x86_64-redhat-linux-gnu,gcc编译)
  • PostgreSQL JDBC Driver postgresql-42.2.19
  • RPGLE 使用 Scott Klement 的 JDBCR4

同步流程概述

通过DB2触发器将变更记录发送到数据队列,RPGLE程序读取队列后,按顺序调用DoPGDeleteMYTABLE和DoPGWriteMYTABLE(因无唯一键,采用先删除PG匹配记录、再重新写入的同步逻辑)。

核心代码

dcl-proc doPostgreConn export;
  dcl-pi doPostgreConn like(connection) end-pi;
  //Code to change and set CLASSPATH variable, if necessary is here
  //...

  prop = JDBC_Properties();
  JDBC_setProp(prop: 'user'         : 'user');
  JDBC_setProp(prop: 'password'     : 'password');
  JDBC_setProp(prop: 'databaseName' : 'database');
  JDBC_setProp(prop: 'loggerLevel'  : 'TRACE'     );
  JDBC_setProp(prop: 'loggerFile'   : '/home/PostgreSQL/log');

  pgconn = JDBC_ConnProp('org.postgresql.Driver'
                     :'jdbc:postgresql://[some_IP]:5432/database'
                     : prop );
  JDBC_freeProp(prop);
  return pgconn;
end-proc;


dcl-proc doPGWriteMyTable export;
  dcl-pi doPGWriteMyTable like(success);
    i#schm char(10);
    i#rec char(334);
  end-pi;

  dcl-ds record extname('MYTABLE') end-ds;
  dcl-s prepStmtTxt varchar(10000);

  record = i#rec;

  pgconn = doPostgreConn;
  if pgconn = *NULL;
    //Custom Error Handling
  endif;

  prepStmtTxt = 'INSERT INTO ' + %trim(i#schm) + '.MYTABLE ' +
    '  VALUES (?, ?, ?) ';

  if PGWriteMYTABLEPrep = *NULL;

    PGWriteMYTABLEPrep = JDBC_PrepStmt(pgconn:prepStmtTxt);

    if PGWriteMYTABLEPrep = *NULL;
        
    endif;
  endif;

  JDBC_setString (PGWriteMYTABLEPrep: 1: StrCol);
  JDBC_setDecimal (PGWriteMYTABLEPrep: 2: DecCol);
  JDBC_setDate (PGWriteMYTABLEPrep: 75: DateCol);

  if JDBC_execPrepUpd( PGWriteMYTABLEPrep ) < 0;
    //Custom Error Handling
  endif;

  JDBC_Close(pgconn);

  return *on;
end-proc;


dcl-proc doPGDeleteMYTABLE export;
  dcl-pi doPGDeleteMYTABLE like(success);
    i#schm char(10);
    i#rec char(334);
  end-pi;

  dcl-ds record extname('MYTABLE') end-ds;
  dcl-s sqlstmt varchar(32000);
  dcl-s deleteSuccess ind;

  record = i#rec;

  sqlstmt = 'DELETE FROM ' + %trim(i#schm) + '.MYTABLE WHERE '; //Basically the key

  pgconn = doPostgreConn;
  if JDBC_ExecUpd(pgconn:sqlstmt) < 0;
    //Custom error handling
  endif;

  DoPostgreClose(pgconn);

  return *on;
end-proc;

问题现象

  1. 首次循环流程:记录更新→删除PG现有记录(成功)→准备写入语句(成功)→写入PG(成功)
  2. 后续循环流程:记录更新→删除PG现有记录(成功)→准备语句(成功)→写入PG(失败)
  3. 上述失败会重复出现,直到重启数据队列任务

错误细节:

  • AS400作业日志报错:org.postgresql.util.PSQLException: This connection has been closed.
  • PG服务器端可见对应连接仍处于打开状态,且RPGLE程序能正常执行连接关闭操作
  • JDBC作业日志仅记录准备语句成功,无写入操作相关日志

排查建议

1. 预编译语句生命周期绑定问题

PGWriteMYTABLEPrep为全局变量,首次循环时绑定了第一个pgconn;但后续循环中pgconn被关闭后,该预编译语句仍持有已关闭的连接引用。即使后续重新创建了新的pgconn,PGWriteMYTABLEPrep并未重新绑定新连接,导致执行写入时使用无效连接。

修复方向:

  • 每次获取新连接后,重新创建预编译语句,取消全局复用逻辑;
  • 在关闭连接前,先调用JDBC_CloseStmt(PGWriteMYTABLEPrep)并将其置为*NULL,确保下次循环重新绑定新连接。

2. 连接资源关闭完整性检查

  • 确认DoPostgreClose函数是否正确清理了连接关联的所有资源(包括预编译语句、结果集等),避免资源泄漏导致连接状态异常;
  • 在doPGWriteMyTable中,关闭连接前必须确保预编译语句已被正确关闭,防止连接关闭后预编译语句仍持有无效引用。

3. JDBC连接属性优化

  • 添加autoReconnect=true连接属性,驱动会自动尝试重连失效的连接;
  • 设置合理的connectTimeout和socketTimeout,避免连接因长时间闲置被PG服务器或防火墙主动断开;
  • 检查PG服务器的tcp_keepalives_idle、tcp_keepalives_interval等参数,调整空闲连接的存活策略。

4. 显式事务控制添加

当前代码未处理事务,首次循环依赖隐式提交成功,但后续循环可能因连接状态异常导致事务无法正常执行。建议在删写操作中添加显式事务:

  • 在doPGDeleteMYTABLE中,执行删除前调用JDBC_SetAutoCommit(pgconn: *OFF),删除成功后执行JDBC_Commit(pgconn),失败则执行JDBC_Rollback(pgconn);
  • doPGWriteMyTable中同样添加事务控制,确保删写操作的原子性,同时避免连接因事务状态异常失效。

5. 日志增强定位问题

  • 在doPGWriteMyTable中添加日志,记录每次获取的pgconn状态(是否为有效连接);
  • 在JDBC_execPrepUpd执行前后添加日志,记录预编译语句绑定的连接标识、参数值等信息,便于定位连接失效的具体时机。

内容的提问来源于stack exchange,提问作者TheLittlePeace

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 07:05:24