触发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;
问题现象
- 首次循环流程:记录更新→删除PG现有记录(成功)→准备写入语句(成功)→写入PG(成功)
- 后续循环流程:记录更新→删除PG现有记录(成功)→准备语句(成功)→写入PG(失败)
- 上述失败会重复出现,直到重启数据队列任务
错误细节:
- 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
相关产品推荐
相关产品推荐

