Skip to content

chore(deps): remove guava from server - #2324

Merged
netomi merged 6 commits into
eclipse-openvsx:mainfrom
trivikr:server-guava
Oct 19, 2026
Merged

netomi merged 6 commits into
eclipse-openvsx:mainfrom
trivikr:server-guava

Conversation

@trivikr

@trivikr trivikr commented Oct 11, 2026

Copy link
Copy Markdown
Contributor

Removes the direct com.google.guava:guava dependency from the server. Guava was used in 8 files, and every call now uses the JDK or a library the server already depends on.

Guava Replacement
Lists.partition subList loops
Iterables.limit / Iterables.any stream().limit(previousSize).noneMatch(...)
Iterables.getLast(Path) small loop helper
ByteStreams.limit bounded read loops in ExtensionService and DrainOnCloseInputStream
Suppliers.memoizeWithExpiration Caffeine LoadingCache
RateLimiter Bucket4j blocking bucket (capacity 1, refilled every 1/rps seconds)

Behaviour change: the mirror rate limit (ovsx.data.mirror.requests-per-second) now spaces calls evenly. Guava let up to one second of requests through at once after an idle period. -1 still means unlimited, and 0 or less (or NaN) still fails at startup.

Guava is still on the runtime classpath through google-cloud-storage.

Copilot AI 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.

🟡 Changes recommended

The Caffeine cache lacks regression coverage, and the fractional-rate test does not exercise token refill behavior.

2 open findings
What changed in this PR

Removes the server’s direct Guava dependency, replacing its remaining usages with JDK, Caffeine, and Bucket4j alternatives.

Changes:

  • Replaces Guava collection and stream utilities with JDK APIs.
  • Reworks bounded stream reads and memoization.
  • Migrates mirror request throttling to Bucket4j with evenly spaced calls.
File Description
server/​build.gradle Removes Guava dependency.
server/​gradle/​libs.versions.toml Removes Guava catalog entries.
server/​CHANGELOG.md Records dependency and rate-limiter changes.
server/​src/​main/​java/​org/​eclipse/​openvsx/​ExtensionService.java Implements bounded package reads.
server/​src/​main/​java/​org/​eclipse/​openvsx/​RegistryAPI.java Replaces iterable duplicate checks.
server/​src/​main/​java/​org/​eclipse/​openvsx/​analytics/​ingestion/​DownloadIngestionMetrics.java Uses Caffeine for metric caching.
server/​src/​main/​java/​org/​eclipse/​openvsx/​analytics/​ingestion/​DownloadIngestionProcessor.java Implements JDK list partitioning.
server/​src/​main/​java/​org/​eclipse/​openvsx/​mirror/​aop/​UpstreamRegistryServiceAspect.java Migrates throttling to Bucket4j.
server/​src/​main/​java/​org/​eclipse/​openvsx/​repositories/​DownloadAnalyticsRepository.java Implements batched sublists.
server/​src/​main/​java/​org/​eclipse/​openvsx/​util/​DrainOnCloseInputStream.java Implements bounded drain loop.
server/​src/​main/​java/​org/​eclipse/​openvsx/​web/​ValidationExceptionHandler.java Finds validation path leaves without Guava.
server/​src/​test/​java/​org/​eclipse/​openvsx/​mirror/​aop/​UpstreamRegistryServiceAspectTest.java Adds rate-limiter tests.
server/​src/​test/​java/​org/​eclipse/​openvsx/​util/​DrainOnCloseInputStreamTest.java Updates bounded-drain coverage.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@netomi
netomi requested a review from cstamas October 14, 2026 07:03
@netomi

netomi commented Oct 14, 2026

Copy link
Copy Markdown
Contributor

@cstamas I guess you are very happy with this change, however we need to make sure that this does not break any existing functionality. Also it might make sense to add useful replacements to our utils package like a limiting bytestream.

@cstamas

cstamas commented Oct 14, 2026

Copy link
Copy Markdown
Member

Yup, Guava is creeping in everywhere while it should not, especially when on Java 25. Will take a peek.

Replace the remaining Guava calls with JDK code, plus Caffeine and
Bucket4j, which the server already depends on:

- Lists.partition, Iterables, ByteStreams.limit: subList, streams and
  bounded read loops
- Suppliers.memoizeWithExpiration: a Caffeine LoadingCache
- RateLimiter: a Bucket4j blocking bucket that spaces mirror calls
  evenly; it no longer lets a burst through after an idle period

Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com>
Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com>
Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com>
Add CollectionUtil.partition and use it in DownloadIngestionProcessor and
DownloadAnalyticsRepository instead of two hand-written subList loops.
DrainOnCloseInputStream.close now drains with skipNBytes.

Document the mirror rate limit change in the changelog: requests are now
spaced evenly instead of allowing a burst after an idle period. Shorten two
comments that described removed code.
@cstamas

cstamas commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Added one commit with a small cleanup.

Also, tried to make guava scope runtime (to ensure nothing picks it up anymore), but google-cloud-storage uses Guava types in its own class signatures, so javac needs them even though our code never imports Guava.

Small fix to PR description: "Guava is still on the runtime compile classpath as transitive dependency..."

cstamas
cstamas previously approved these changes Oct 18, 2026
@cstamas
cstamas requested a review from netomi October 18, 2026 09:35

@netomi netomi 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.

Two points on the replacements, otherwise the swap looks equivalent to me.

1. The rate limiter is now interruptible (UpstreamRegistryServiceAspect)

bucket.consume(1) throws InterruptedException when the waiting thread is interrupted. Guava's RateLimiter.acquire() slept uninterruptibly and never threw. With the PR's bucket configuration on Bucket4j 8.10.1, an interrupt during the wait throws InterruptedException and leaves the interrupt flag cleared. The advice is declared throws Throwable, so the exception now escapes into the mirror code, and a caller that catches it generically loses the interrupt status.

BlockingBucket#consumeUninterruptibly(long) exists in 8.10.1 and keeps the previous behaviour. If the new behaviour is intended, it is worth a line in the changelog entry next to the even-spacing change.

For reference, I ran the same configuration standalone: 12 concurrent calls at 10 rps took about 1101 ms, and 3 calls after an idle period took about 200 ms, so the even spacing and the absence of a stored burst match the description.

2. DrainOnCloseInputStream#close no longer updates bytesRead

skipNBytes goes through FilterInputStream#skip, which delegates to the wrapped stream and bypasses read, so bytesRead is not advanced by the drain. This matters because close() appears to run twice on this stream: once from the try-with-resources in LocalRegistryService and once from try (var input = content) in createExtensionFile. The second close skips again on an already-closed stream. The resulting IOException is swallowed, so it works in practice, but close() is no longer a no-op the second time, and a runtime exception from a closed stream would not be caught. I did not confirm that LocalRegistryService passes the same stream into createExtensionFile.

Reading through this (for example readNBytes into a discarded buffer, or a small loop on read(byte[])) would keep bytesRead accurate and make the second close cheap, as before.

@netomi
netomi merged commit d5221fb into eclipse-openvsx:main Oct 19, 2026
5 checks passed
@trivikr
trivikr deleted the server-guava branch October 19, 2026 14:09
netomi pushed a commit that referenced this pull request Oct 19, 2026
The guava removal and the mirror rate limit change were merged after
v1.3.0 was tagged, but renaming [next] to [1.3.0] listed them under
the release.

Signed-off-by: Trivikram Kamat <16024985+trivikr@users.noreply.github.com>
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.

4 participants