Tentative claude fix of portal C_34 error#55
Conversation
|
I thought the fix looked a bit cut-and-paste so I asked Claude Opus to look for a more elegant solution. This would mean some changes to FgpUtil. See below The research shows that That said, there are two options for making this more elegant: Option 1: Add an overload in FgpUtil (upstream fix)Add a return ResultSets.openStream(dbInstance.getDataSource(), sql,
"Produce variable distribution",
new QueryFlags()
.setCommitAndCloseFlag(CommitAndClose.CALLER_IS_RESPONSIBLE)
.setFetchSize(0),
row -> Optional.of(new TwoTuple<>(
distributionVariable.getType().convertRowValueToStringValue(row),
row.getLong(COUNT_COLUMN_NAME))));This keeps the simplicity of Option 2: Keep the current fix as-isThe current implementation is actually the idiomatic approach given the existing API. It's explicit about what's happening and the comment explains why. The only minor cleanup would be dropping the unnecessary empty args: return new SQLRunner(dbInstance.getDataSource(), sql, "Produce variable distribution")
.executeQuery(
new QueryFlags()
.setCommitAndCloseFlag(CommitAndClose.CALLER_IS_RESPONSIBLE)
.setFetchSize(0),
rs -> new ResultSetStream<>(rs, row -> Optional.of(
new TwoTuple<>(distributionVariable.getType().convertRowValueToStringValue(row),
row.getLong(COUNT_COLUMN_NAME)))));(Using the Bottom line: The current fix is correct and reasonable. If you want it cleaner at this call site, a small FgpUtil PR adding a |
Another update: first proposed changes were using newer a
FgpUtilversion which hasn't yet been rolled out for this repo. The change suggested now is much simpler, but may cause memory issues. Leaving this to @ryanrdoherty and others to take care of.(To reproduce the bug, go to any
DEsearch on beta.plasmodb.org, open up the subsetting cell and browse the variables on the counts entities.)Claude Sonnet 4.6 took 45 minutes and one whole context window to figure this out!
(Opus review as a comment below)
Root Cause
In lib-eda-subsetting v7.0.0 (
pg-supportbranch),FilteredResultFactory.produceVariableDistribution()was changed from accepting a rawDataSourceto aDatabaseInstance. UsingdbInstance.getDataSource()routes connections throughConnectionWrapper, which callsapplyFetchSize(200)on everyprepareStatement(). Combined withQueryExecutor.setAutocommit()always settingautocommit=false, this activates PostgreSQL JDBC server-side cursors.The JDBC driver then declares
DECLARE C_34 CURSOR FOR <query>and fetches rows in batches of 200. For variables with ≤200 distinct values this is invisible — only one batch is needed. ForSEQUENCE_READ_COUNT_SENSEinpfal3D7_GENE_EXPRESSION_RNASEQ_DATA(which has >200 distinct values), the second batch fetch arrives to find portal "C_34" no longer exists on the server.This failure mode is explicitly documented in
PreparedStatementExecutor.setAutocommit():What is a Portal?
A portal in PostgreSQL is a named server-side cursor — an execution context holding the state of a running query, allowing rows to be fetched incrementally in batches.
When PostgreSQL JDBC uses server-side streaming (
fetchSize > 0,autocommit=false), it:DECLARE C_34 CURSOR FOR <query>to create the portalFETCH 200 FROM C_34Portals are transaction-scoped. When the transaction commits or rolls back, all non-
WITH HOLDportals are immediately destroyed. "Portal C_34 does not exist" means the cursor was declared, the first batch fetched successfully, but by the time the next batch was requested, the transaction had ended.The Fix
FilteredResultFactory.produceVariableDistribution()inlib-eda-subsetting— replaceResultSets.openStream()with a directSQLRunnercall usingQueryFlags.setFetchSize(0). This overridesConnectionWrapper's default of 200, disabling server-side cursors for this query. Distribution queries return aggregated data (one row per distinct value), so loading all rows at once is safe.