Skip to content

Conversation

@abstratt
Copy link
Member

@abstratt abstratt commented Dec 16, 2025

Issue: #36046 (see reproducer there)

Without the fix:

5000 problems were found storing the configuration cache, only the first 4096 were considered, 1 of which seems unique.
- Build file 'child/build.gradle': line 2: Project ':child' cannot dynamically look up a property in the parent project ':'

Report size: 107M

With the fix:

5000 problems were found storing the configuration cache, 1 of which seems unique.
- Build file 'child/build.gradle': line 2: Project ':child' cannot dynamically look up a property in the parent project ':'

Report size: 153K

Reviewing cheatsheet

Before merging the PR, comments starting with

  • ❌ ❓must be fixed
  • 🤔 💅 should be fixed
  • 💭 may be fixed
  • 🎉 celebrate happy things

@abstratt abstratt added this to the 9.4.0 RC1 milestone Dec 16, 2025
@abstratt

This comment has been minimized.

@bot-gradle

This comment has been minimized.

@abstratt abstratt force-pushed the rchaves/master/max-unique-problems branch from 03fa21f to 0f1ecef Compare December 16, 2025 17:42
@abstratt abstratt self-assigned this Dec 16, 2025
withProblem("Task `:problems` of type `BrokenTask`: cannot serialize object of type 'org.gradle.api.internal.project.DefaultProject', a subtype of 'org.gradle.api.Project', as these are not supported with the configuration cache.")
totalProblemsCount = 6
problemsWithStackTraceCount = 2
problemsWithStackTraceCount = 1
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are unique counts now

addNotReportedDegradingTasks()
addDegradingFeatures()
val summary = summarizer.get()
val hasNoProblemsForConsole = summary.reportableProblemCount == 0
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got confused by reportableProblemCount - it really means a count of problems we report to the console

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry 😄

@abstratt abstratt force-pushed the rchaves/master/max-unique-problems branch from 0f1ecef to 0b30add Compare December 16, 2025 17:58
val lock = ReentrantLock()

fun get(): Summary = lock.withLock {
Summary(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Many parameters here have same type, wanted to make the data flows more explicit

totalProblemCount = totalProblemCount,
uniqueProblemCount = problemCauses.size,
deferredProblemCount = deferredProblemCount,
consoleProblemCount = totalProblemCount - suppressedSilentlyProblemCount,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an actual change

uniqueProblemCount = problemCauses.size,
deferredProblemCount = deferredProblemCount,
consoleProblemCount = totalProblemCount - suppressedSilentlyProblemCount,
consoleProblemCauses = ImmutableMap.copyOf(problemCauses.filterValues { it != ProblemSeverity.SuppressedSilently }),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So is this. Since we only care for problem causes we can show on the console, decided to filter it here, and reflect that in the property name

overflowed = true
return false
}
if (severity != ProblemSeverity.SuppressedSilently) {
Copy link
Member Author

@abstratt abstratt Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, we would not apply deduplication to problems with this severity, now we do

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Do we cover this by integTest?

Copy link
Member Author

@abstratt abstratt Dec 17, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do with a unit test:

Also, integration tests had to be updated to reflect this, including a smoke test.

fun onIncompatibleFeature() {
fun onIncompatibleFeature(problem: PropertyProblem) {
lock.withLock {
onProblem(problem, ProblemSeverity.SuppressedSilently)
Copy link
Member Author

@abstratt abstratt Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For incompatible features, we were adding a problem to the report without letting the summarizer know - now we do report the problem so it is reflected on totalProblemCount and suppressedSilentlyProblemCount.

* Returns whether problem was accepted.
*/
private
fun recordProblemCause(problem: PropertyProblem, severity: ProblemSeverity): Boolean {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before, this may return false even if the new problem upgraded the severity of a previous one

@abstratt abstratt force-pushed the rchaves/master/max-unique-problems branch 2 times, most recently from be5eed3 to fad4665 Compare December 16, 2025 18:19

assertThat(
"HTML report JS model has wrong number of total problem(s)",
numberOfProblems(),
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this was relying on number of problems in the data - now that we deduplicate, we need to query the totalProblemCount slot instead

}

private void assertProblemsHtmlReport(HasConfigurationCacheProblemsSpec spec) {
def totalProblemCount = spec.totalProblemsCount ?: spec.uniqueProblems.size()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this meant that if totalProblemsCount == 0 we would ignore it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Not sure, why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because 0 ?: something is always something (0 is the numerical falsy value)

)

if (spec.checkReportProblems) {
def problemMessages = problemMessages().unique()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no need to remove duplicates here

@bot-gradle

This comment has been minimized.


int getProblemsWithStackTrace() {
return problems.inject(0) { a, b -> a + (b.hasStackTrace ? b.count : 0) }
return problems.inject(0) { a, b -> a + (b.hasStackTrace ? 1 : 0) }
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This count is unique now

@abstratt abstratt force-pushed the rchaves/master/max-unique-problems branch from fad4665 to 997b249 Compare December 16, 2025 18:37
@abstratt abstratt marked this pull request as ready for review December 16, 2025 18:39
@abstratt abstratt requested review from a team as code owners December 16, 2025 18:39
@abstratt abstratt requested review from 6hundreds and bamboo and removed request for a team December 16, 2025 18:39
@cobexer cobexer removed the request for review from a team December 16, 2025 18:40
@abstratt

This comment has been minimized.

@bot-gradle

This comment has been minimized.

@bot-gradle
Copy link
Collaborator

The following builds have failed:

"Project ':' cannot access 'Project.plugins' functionality on subprojects via 'allprojects'",
"Project ':' cannot access 'Project.extensions' functionality on subprojects via 'allprojects'",
)
// maximum number of problems we collect (should be 86520)
Copy link
Member Author

@abstratt abstratt Dec 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was what made us notice the issue with lack of deduplication. I think 86520 was what I saw with a previous version of the init script used by this test which would fail to schedule some tasks. When that was addressed, and more tasks scheduled grew, so did the number of violations.

@abstratt
Copy link
Member Author

@bot-gradle test RFN

@bot-gradle

This comment has been minimized.

@bot-gradle
Copy link
Collaborator

The following builds have passed:

@abstratt abstratt force-pushed the rchaves/master/max-unique-problems branch from ad6ef3d to 1ac66b7 Compare December 17, 2025 03:01
Comment on lines -242 to +244
totalProblemsCount = 2
// gracefully degraded task appears in the report
withProblem(INVOCATION_OF_TASK_PROJECT_AT_EXECUTION_TIME)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Why don't we check total problems count anymore?

🤔 I wonder, why do we need to add the same withProblem(xxx)? The deal is withProblems adds a problem to uniqueProblems set, so I'd assume that it should be unique?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do (it is always checked), but since it is the same as the number of unique problems, it is not necessary to explicitly state it.

The set of expected unique problems is actually a list. Each expected unique problem must match each actual unique problem message, in the same position they are expected to appear. The matching is based on message alone, not location, but uniqueness considers both message and location, so the same kind of problem (with identical messages) may appear multiple times.

I think Mike's PR will allow us to match locations as well.

* Builds the console feedback string for configuration cache problems.
*
* Rules:
* - If there are reportable problems, print a summary header and a curated list of up to [MAX_CONSOLE_PROBLEMS].
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Should we use "console" instead of "reportable" ?

Comment on lines 238 to 274
if (reportableProblemCauseCount > MAX_CONSOLE_PROBLEMS) {
appendLine("plus ${reportableProblemCauseCount - MAX_CONSOLE_PROBLEMS} more problems. Please see the report for details.")
if (uniqueProblemCount > MAX_CONSOLE_PROBLEMS) {
appendLine("plus ${uniqueProblemCount - MAX_CONSOLE_PROBLEMS} more problems. Please see the report for details.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ Did we change the logic here?
Before, we were checking reportableProblemCauseCount > MAX_CONSOLE_PROBLEMS, and then print "plus ${reportableProblemCauseCount - MAX_CONSOLE_PROBLEMS} more problems."
Now we do same, but instead of reportableProblems we use uniqueProblems, which includes suppressed silently problems

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks! I will add a test and fix this.

Comment on lines +35 to +37
const val REPORT_URL: String = "<<REPORT_URL_PLACEHOLDER>>"

const val ACTION: String = "<<ACTION_PLACEHOLDER>>"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❓ How these placeholder get populated?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are used as fixed values for report URL and action strings, respectively, and make the actual output predictable.

val subject = ConfigurationCacheProblemsSummary(maxCollectedProblems = 2)
val subject = ConfigurationCacheProblemsSummary(maxCollectedProblems = 3)

// causes for unique problems are all collected
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Not sure I understand the comment

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will try to improve it in my next commit

Comment on lines +56 to +59
assertThat(
subject.get().uniqueProblemCount,
equalTo(1)
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💅 Maybe `assert(subject.get().uniqueProblemCount == 1) is more concise?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indeed, or assertEquals... I tried to stay with the flavor of assertions in place as much as possible

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no assertEquals on the classpath there tho :(

Copy link
Member

@6hundreds 6hundreds left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job! Left some comments

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.

Configuration cache report threshold for unique problems is triggered by non-unique problems

4 participants