Skip to content

fix: prevent ConfigurationCacheHackList fingerprint races on P2 (#3004) - #3006

Merged
nedtwigg merged 5 commits into
diffplug:mainfrom
arimu1:fix/3004-configcachehacklist-p2-serialize
Aug 15, 2026
Merged

fix: prevent ConfigurationCacheHackList fingerprint races on P2 (#3004)#3006
nedtwigg merged 5 commits into
diffplug:mainfrom
arimu1:fix/3004-configcachehacklist-p2-serialize

Conversation

@arimu1

@arimu1 arimu1 commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes #3004.

In multi-project builds, Gradle fingerprints many Spotless tasks in parallel. Each fingerprint serializes the @Input stepsInternalEquality (ConfigurationCacheHackList.forEquality()), which eagerly resolves equality state for Eclipse/P2-backed steps (eclipse(), greclipse(), …). Concurrent Solstice P2 queries race on the on-disk cache and fail with:

Cannot fingerprint input property 'stepsInternalEquality':
  value 'com.diffplug.spotless.ConfigurationCacheHackList@…' cannot be serialized.
> java.io.IOException: Failed to provision P2 dependencies

This matches the intermittent failure seen in junit-framework (:junit-jupiter-api:spotlessJava with eclipse() under Gradle 9.7 / Spotless 8.9.0) and earlier reports such as #2331.

Changes

  1. P2Provisioner.createDefault() — serialize all P2 queries on P2Provisioner.class so Solstice’s disk cache is not accessed concurrently.
  2. SpotlessTaskService — share one DedupingP2Provisioner across subprojects (not per-project) so parallel fingerprinting reuses a single in-memory cache + lock.
  3. ConfigurationCacheHackList.toString() — do not call hashCode(). Default Object.toString() fingerprints every step (may provision P2 again) while Gradle formats the “cannot be serialized” message, masking the real cause (see Spotless Gradle plugin 7.0.0: Eclipse / p2 download race condition #2331 stack traces).

Test plan

  • ./gradlew :testlib:test --tests com.diffplug.spotless.ConfigurationCacheHackListTest (2/2)
  • ./gradlew :plugin-gradle:test --tests com.diffplug.gradle.spotless.GradleProvisionerTest (19/19)
  • ./gradlew :plugin-gradle:test --tests com.diffplug.gradle.spotless.ConfigurationCacheTest (3/3)
  • ./gradlew spotlessApply on touched sources

arimu1 and others added 5 commits August 1, 2026 10:45
Gradle fingerprints stepsInternalEquality by serializing ConfigurationCacheHackList,
which eagerly provisions Eclipse/P2 jars. Parallel multi-project builds raced on
Solstice's disk cache and failed with "cannot be serialized" / "Failed to provision
P2 dependencies" (diffplug#3004).

- Serialize P2 queries process-wide in P2Provisioner.createDefault()
- Share one DedupingP2Provisioner across subprojects in SpotlessTaskService
- ConfigurationCacheHackList.toString() no longer calls hashCode() (avoids
  re-provisioning while Gradle formats the error message)

Fixes diffplug#3004
…acklist-p2-serialize

# Conflicts:
#	CHANGES.md
#	plugin-gradle/CHANGES.md
#	plugin-maven/CHANGES.md
lib-extra's `enforceSolsticeVersion` step requires the hardcoded
"dev.equo.ide:solstice:X" in P2Provisioner to match libs.versions.toml,
which is already at 1.8.2. main is inconsistent here, but ratchetFrom
'origin/main' hides it until a PR touches this file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit made ConfigurationCacheHackList.toString() side-effect
free, which was correct but load-bearing: Gradle renders the value into
"Cannot fingerprint input property 'stepsInternalEquality': value '...'
cannot be serialized" and discards the cause. The old default toString()
called the overridden hashCode(), which evaluated the steps and threw the
actionable GradleException ("P2 dependencies not predeclared. Add Eclipse
formatter configuration to the `spotlessPredeclare` block...") out of
Gradle's message formatting, replacing the generic message.

Removing that side effect left users with only "cannot be serialized" and
broke 4 tests. Gradle builds that message only after serialization has
already thrown, so record the failure in writeObject and report it from
toString() instead. The message survives without toString() ever
evaluating step state.

Fixes SpotlessPredeclareIntegrationTest (P2/Maven/Groovy predeclareFails*)
and MultiProjectTest.predeclaredFails.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sanity Check has been failing on nearly every PR with:

  Cannot fingerprint input property 'stepsInternalEquality':
  ... cannot be serialized
  > java.io.IOException: Failed to provision P2 dependencies

Parallel fingerprinting of our own eclipse()/greclipse() steps races
Solstice's on-disk P2 cache. We format ourselves with the published
plugin pinned in settings.gradle, so the fix in this branch cannot help
our CI until it is released and that pin is bumped. Turn parallel
fingerprinting off until then.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nedtwigg
nedtwigg merged commit a42094e into diffplug:main Aug 15, 2026
20 checks passed
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.

ConfigurationCacheHackList cannot be serialized

2 participants