Skip to content

Disable LabKey caching of ResultSet by default#7711

Merged
labkey-jeckels merged 9 commits into
developfrom
fb_disableCacheByDefault
Jun 6, 2026
Merged

Disable LabKey caching of ResultSet by default#7711
labkey-jeckels merged 9 commits into
developfrom
fb_disableCacheByDefault

Conversation

@labkey-jeckels

@labkey-jeckels labkey-jeckels commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Rationale

We don't want to cache more than we need to. We just disabled Postgres JDBC caching for QueryService.getSelectBuilder() scenarios. Most scenarios shouldn't need a LabKey cache either.

Changes

  • Stop using CachedResultSet by default
  • Opt in to caching when needed
  • Tests for result set scrollability
  • Improve generics
  • Migrate remaining QueryService.select() and QueryService.selector() methods to QueryService.getSelectBuilder()

Tasks 📍

  • Claude Code Review
  • Manual Testing
  • Test Automation

@labkey-jeckels labkey-jeckels self-assigned this Jun 2, 2026
@labkey-jeckels

Copy link
Copy Markdown
Contributor Author

labkey-jeckels and others added 2 commits June 3, 2026 20:50
QueryHelper funnels every select through SelectBuilder.select(), whose default flipped to an uncached, streaming result set on this branch. Many QueryHelper callers (15 files across wnprc-modules, ehrModules, OConnorLabModules, and premiumModules) rely on the cached result set's full API — particularly getRowMap(), which a streaming result set does not support. In wnprc-modules' SimpleQuery the resulting UnsupportedOperationException is swallowed and surfaces as an empty result, causing WNPRC_EHRTest's consistent "Account acct103 not found in aliases table" setup failure on this branch. Request a cached result set explicitly to preserve the helper's longstanding semantics.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

@labkey-adam labkey-adam left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments to consider, but no further review required

tableMap.put("__DATA", assayDataTable);

try (ResultSet rs = QueryService.get().select(schema, sqlf.getSQL(), tableMap, false, true))
try (ResultSet rs = QueryService.get().getSelectBuilder(schema, sqlf.getSQL(), false, tableMap).select(true))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't we want getSelectBuilder() to take a SQLFragment?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@labkey-matthewb and I discussed this. Ultimately, we want it to take a LabKeySQLFragment or similar and keep SQLFragment for raw DB SQL.

ArrayList<String> ids = new ArrayList<>();
while (rs.next())
ids.add(rs.getString(1));
return ids;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we'd convert this all to .buildSqlSelector().getArrayList(String.class); Maybe that's for another day.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I don't want to figure out how to test this right now.

default Results select()
{
return select(true);
return select(false);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe too late for this, but it would be nice to replace the various boolean parameters with enums that elucidate the behaviors.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea for next time.

return select.select();
// select(true): legacy callers (especially EHR modules) rely on the cached result set's full API,
// e.g. getRowMap(), which a streaming result set does not support
return select.select(true);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Boo

dcMySampleParent = mySampleParent.getRenderer();

assertTrue(results.next());
ctx.setRow(results.getRowMap());

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Calling getRowMap() requires a cached Results, right? Should we document that method as such?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaDoc added.

}
if (!_container.equals(that._container)) return false;
return _schema.equals(that._schema);
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the default equals() essentially this?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems odd to remove hashCode() but not equals()

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Removed. I told IntelliJ to automatically apply some auto-refactors on save and it's surprisingly aggressive at converting some classes to records.

return false;

return true;
if (obj instanceof SessionQuery(String sql, String metadata))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Whoa, what is this strange syntax? Is that strictly a record thing?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, default equals() won't do here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing. Simplified.

@labkey-jeckels labkey-jeckels merged commit b47a842 into develop Jun 6, 2026
11 checks passed
@labkey-jeckels labkey-jeckels deleted the fb_disableCacheByDefault branch June 6, 2026 16:05
labkey-jeckels added a commit to LabKey/wnprc-modules that referenced this pull request Jun 6, 2026
#### Rationale
As we work to reduce OutOfMemoryError risk, we need to have places that
rely on caching Results behavior opt-in. We want to consolidate on using
a single builder approach for these selects.

#### Related Pull Requests
* LabKey/platform#7711

#### Changes
* Migrate to `QueryService.getSelectBuilder()`
labkey-jeckels added a commit to LabKey/targetedms that referenced this pull request Jun 6, 2026
#### Rationale
As we work to reduce OutOfMemoryError risk, we need to have places that
rely on caching Results behavior opt-in. Here, it's so that we can ask
the size of the result set before iterating it.

#### Related Pull Requests
* LabKey/platform#7711

#### Changes
* Opt-in to a `CachedResultSet`
labkey-jeckels added a commit to LabKey/commonAssays that referenced this pull request Jun 6, 2026
#### Rationale
As we work to reduce OutOfMemoryError risk, we need to have places that
rely on caching Results behavior opt-in. Here, it's so that we can ask
the size of the result set before iterating it.

#### Related Pull Requests
* LabKey/platform#7711

#### Changes
* Opt-in to a `CachedResultSet`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants