Skip to content

GitHub Issue 1187: Auto-disable PostgreSQL JDBC result set caching#7833

Open
labkey-jeckels wants to merge 14 commits into
developfrom
fb_1187_selectorCaching
Open

GitHub Issue 1187: Auto-disable PostgreSQL JDBC result set caching#7833
labkey-jeckels wants to merge 14 commits into
developfrom
fb_1187_selectorCaching

Conversation

@labkey-jeckels

@labkey-jeckels labkey-jeckels commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Rationale

The Postgres JDBC driver caches ResultSets in memory by default for our configuration. We want to opt-out more aggressively to avoid OutOfMemoryError and performance problems.

Changes

  • Flip the default for SqlExecutingSelector to not cache by default
  • Reuse connections when in the midst of another read-only query but outside of a transaction
    • Example: while iterating a ResultSet, do a followup query per row (bad for perf, but not something we should prohibit or use two connections to accomplish)
  • Warn when code pulls back a large ArrayList of results

@labkey-jeckels labkey-jeckels self-assigned this Jul 10, 2026
@labkey-jeckels
labkey-jeckels marked this pull request as ready for review July 20, 2026 16:59
@labkey-jeckels

Copy link
Copy Markdown
Contributor Author

Interested in feedback related to connection reuse and the warnings for returning long ArrayLists


// Throttles the large-result warning to at most once per day per unique call stack, so a legitimately large (but
// expected) load doesn't flood the log. Keyed by a signature of the call stack; see getArrayList().
private static final Cache<String, Boolean> LARGE_RESULT_WARNING_THROTTLE = CacheManager.getCache(1000, CacheManager.DAY, "SqlSelector large result warnings");

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.

A Throttle is more convenient than a generic Cache

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.

Switched.

* (here plus, potentially, in the JDBC driver's buffer) is a common source of OutOfMemoryErrors; callers should
* generally prefer a streaming method — {@link #forEach(Class, Selector.ForEachBlock)}, {@link #forEachBatch}, or {@link #uncachedStream} — that
* processes rows without materializing them all at once. {@code getArray}, {@code getCollection},
* {@code getMapArray}, and {@code getMapCollection} all delegate here, so they're covered as well.

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.

FYI: BaseSelector.stream() calls this as well

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.

Comment adjusted. Unexpected that stream() isn't actually streaming.

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.

Well, it is a Java Stream. The trade-off is there's no way for a Stream to close resources (e.g., ResultSet, Connection) after exhausting/terminal operation. So either you need to get the stream via try-with-resources (required for our uncached variants) or stream an in-memory data structure. These methods are well documented. We could reverse the paradigm (cachedStream(), stream()). Or require every stream() caller to close the stream. It's just not super intuitive when folks aren't used to closing streams. Devs have messed this up repeatedly with Files.find(), e.g.

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.

2 participants