Skip to content

GH-173: Preserve concurrent JDK cache updates - #204

Open
efegokdemir wants to merge 3 commits into
apache:masterfrom
efegokdemir:efe/toolchains-cache-race-173
Open

efegokdemir wants to merge 3 commits into
apache:masterfrom
efegokdemir:efe/toolchains-cache-race-173

Conversation

@efegokdemir

@efegokdemir efegokdemir commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Closes #173.

Concurrent JDK discovery can add a cache entry while another call is writing the cache. The write then clears the modified flag, so that entry is not persisted.

Cache updates now coordinate with the snapshot used for persistence. A failed write leaves the cache marked dirty, and concurrent discoveries of the same JDK reuse the first cached model. A latch-controlled regression test reproduces the lost update and verifies that the next write persists it.

Validation:

  • JAVA_HOME=/opt/homebrew/opt/openjdk@21 mvn clean -Dspotless.skip=true -Dtest=ToolchainDiscovererTest test — passed (2 tests).
  • JAVA_HOME=/opt/homebrew/opt/openjdk@21 mvn spotless:check — passed.
  • JAVA_HOME=/opt/homebrew/opt/openjdk@21 mvn -Prun-its verify — passed (8 integration tests).
  • Regression test failed on the unmodified implementation because the concurrent JDK entry was absent from the cache file.
  • git diff --check — passed.

To make clear that you license your contribution under the Apache License Version 2.0, January 2004, check one of the following:

@elharo elharo added the bug Something isn't working label Oct 17, 2026
@elharo
elharo requested a balanced review from Copilot October 17, 2026 11:54
try {
Path cacheFile = getCacheFile();
Files.createDirectories(cacheFile.getParent());
try (Writer w = Files.newBufferedWriter(cacheFile)) {

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.

w --> writer

Path cacheFile = getCacheFile();
Files.createDirectories(cacheFile.getParent());
try (Writer w = Files.newBufferedWriter(cacheFile)) {
PersistedToolchains pt = new PersistedToolchains();

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 abbreviate variable names

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 regression test needs stronger synchronization because it can pass against the original implementation.

1 open issue
What changed in this PR

Addresses #173 by coordinating JDK cache updates with persistence snapshots to prevent lost concurrent updates.

Changes:

  • Synchronizes snapshot creation and cache insertion, reusing existing models.
  • Keeps the cache dirty after failed writes.
  • Adds a latch-controlled concurrency regression test.
File Description
src/​test/​java/​org/​apache/​maven/​plugins/​toolchain/​jdk/​ToolchainDiscovererTest.java Adds concurrent cache-persistence regression coverage.
src/​main/​java/​org/​apache/​maven/​plugins/​toolchain/​jdk/​ToolchainDiscoverer.java Coordinates updates and snapshots and preserves retry state.

Review effort: Balanced

Comment on lines +112 to +113
assertTrue(discoverer.secondModelCreated.await(10, TimeUnit.SECONDS), "cache update did not start");
cache.allowSnapshot.countDown();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Race condition on cacheModified flag in ToolchainDiscoverer from parallelStream()

3 participants