Summary
On each Multibranch/Organization Folder scan, the plugin lists open PRs via GET /repos/{owner}/{repo}/pulls?state=open. The github-api client is configured with an OkHttp cache that supports conditional requests (If-None-Match/ETag → 304), but the cache is not reused between scans — so nearly every scan sends an unconditional request and gets a full 200 even when the list is unchanged.
Cause: a pooled connection (and its OkHttp Cache) is discarded after 30 min idle, while scans run less often (hourly by default). The next scan can't reuse the previous scan's cache.
Details (master @ fa27ed96)
UnusedConnectionDestroyer evicts pooled connections idle > 30 min (L619-634). On eviction, removeAllUnused() handles the Cache by credential type (L694-715):
- GitHub App creds:
cleanupCacheFolder=true (L423) → eviction runs cache.delete() (L700-702), so the next scan starts from an empty directory.
- PAT / other creds:
cleanupCacheFolder=false, so that branch is skipped and the Cache is neither deleted nor close()d — it's dropped still-open. The next scan opens a new Cache on the same directory; it's unclear whether entries from the prior (never-closed) Cache are reliably reused here, and conditional requests do not appear to be sent in this path. (Worth confirming with a reproduction.)
The cache is also disabled by default on Windows (cacheSize = isWindows() ? 0 : 20, L168-170).
Why it matters
GitHub doesn't count 304 responses against the REST rate limit (docs). Reusing the cache means unchanged PR lists return 304 — no quota spent, no body transferred. For controllers scanning many repos on a short interval, that lowers rate-limit consumption (and the back-off it triggers) and cuts redundant transfer/parsing.
Benefit scales with how often listings are unchanged; changed listings still return 200 as today. This restores existing-but-inert caching and doesn't change scan results.
Proposed fix
1. Preserve the cache between scans. On eviction, always close() the Cache (flush + release) but never delete(), for all credential types. It's already cacheSize-bounded (LRU) and keyed by a stable per-credential hash, so keeping it on disk is bounded/safe:
- if (record.cache != null && record.cleanupCacheFolder) {
- record.cache.delete();
- record.cache.close();
- }
+ if (record.cache != null) {
+ record.cache.close();
+ }
The next scan can then reopen a warm cache and send If-None-Match. The cleanupCacheFolder flag can be removed.
2. (Optional) Enable the cache on Windows — default cacheSize to 20 instead of isWindows() ? 0 : 20.
Validation
Regression test: prime the cache, force eviction, re-request, assert the cache is reused / If-None-Match is sent — for both GitHub App and PAT credentials.
Summary
On each Multibranch/Organization Folder scan, the plugin lists open PRs via
GET /repos/{owner}/{repo}/pulls?state=open. Thegithub-apiclient is configured with an OkHttp cache that supports conditional requests (If-None-Match/ETag→304), but the cache is not reused between scans — so nearly every scan sends an unconditional request and gets a full200even when the list is unchanged.Cause: a pooled connection (and its OkHttp
Cache) is discarded after 30 min idle, while scans run less often (hourly by default). The next scan can't reuse the previous scan's cache.Details (
master@fa27ed96)UnusedConnectionDestroyerevicts pooled connections idle > 30 min (L619-634). On eviction,removeAllUnused()handles theCacheby credential type (L694-715):cleanupCacheFolder=true(L423) → eviction runscache.delete()(L700-702), so the next scan starts from an empty directory.cleanupCacheFolder=false, so that branch is skipped and theCacheis neither deleted norclose()d — it's dropped still-open. The next scan opens a newCacheon the same directory; it's unclear whether entries from the prior (never-closed)Cacheare reliably reused here, and conditional requests do not appear to be sent in this path. (Worth confirming with a reproduction.)The cache is also disabled by default on Windows (
cacheSize = isWindows() ? 0 : 20, L168-170).Why it matters
GitHub doesn't count
304responses against the REST rate limit (docs). Reusing the cache means unchanged PR lists return304— no quota spent, no body transferred. For controllers scanning many repos on a short interval, that lowers rate-limit consumption (and the back-off it triggers) and cuts redundant transfer/parsing.Benefit scales with how often listings are unchanged; changed listings still return
200as today. This restores existing-but-inert caching and doesn't change scan results.Proposed fix
1. Preserve the cache between scans. On eviction, always
close()theCache(flush + release) but neverdelete(), for all credential types. It's alreadycacheSize-bounded (LRU) and keyed by a stable per-credential hash, so keeping it on disk is bounded/safe:The next scan can then reopen a warm cache and send
If-None-Match. ThecleanupCacheFolderflag can be removed.2. (Optional) Enable the cache on Windows — default
cacheSizeto20instead ofisWindows() ? 0 : 20.Validation
Regression test: prime the cache, force eviction, re-request, assert the cache is reused /
If-None-Matchis sent — for both GitHub App and PAT credentials.