Skip to content

Fix recursive update issue in SmartProjectComparator#10997

Merged
gnodet merged 1 commit intoapache:masterfrom
gnodet:fix-recursive-update-issue-10995
Aug 6, 2025
Merged

Fix recursive update issue in SmartProjectComparator#10997
gnodet merged 1 commit intoapache:masterfrom
gnodet:fix-recursive-update-issue-10995

Conversation

@gnodet
Copy link
Copy Markdown
Contributor

@gnodet gnodet commented Jul 25, 2025

Description

Fixes #10995 - Resolves IllegalStateException: Recursive update in SmartProjectComparator during parallel builds of large multi-module projects.

Problem

The SmartProjectComparator.getProjectWeight() method was using ConcurrentHashMap.computeIfAbsent() in a recursive context. When calculateWeight() calls getProjectWeight() for downstream projects, it creates recursive computeIfAbsent() calls that violate ConcurrentHashMap's internal constraints, leading to:

java.lang.IllegalStateException: Recursive update
    at java.base/java.util.concurrent.ConcurrentHashMap.computeIfAbsent(ConcurrentHashMap.java:1760)
    at org.apache.maven.lifecycle.internal.builder.multithreaded.SmartProjectComparator.getProjectWeight(SmartProjectComparator.java:83)

Solution

  • Replace computeIfAbsent() with explicit get() + putIfAbsent() pattern
  • Eliminate recursive calls to computeIfAbsent() that cause the exception
  • Maintain thread safety using putIfAbsent() for concurrent access
  • Preserve functionality - same logic, safer implementation

Changes

Core Fix

public long getProjectWeight(MavenProject project) {
    // First check if weight is already calculated
    Long existingWeight = projectWeights.get(project);
    if (existingWeight != null) {
        return existingWeight;
    }
    
    // Calculate weight without using computeIfAbsent to avoid recursive update issues
    long weight = calculateWeight(project);
    
    // Use putIfAbsent to handle concurrent access safely
    Long previousWeight = projectWeights.putIfAbsent(project, weight);
    return previousWeight != null ? previousWeight : weight;
}

Test Coverage

  • Added comprehensive concurrent test case that reproduces the issue
  • Verifies the fix works under high concurrency (50 threads, 1000 iterations)
  • Tests complex dependency graphs that trigger the recursive scenario

Testing

Verified with reproducer: Used maven-multiproject-generator (4383 modules, 6568 dependencies)
Concurrent stress test: 50 threads × 1000 iterations - no recursive update exceptions
Functionality preserved: All existing weight calculations work correctly
Thread safety maintained: Safe concurrent access to project weights

Impact

  • Fixes critical issue affecting large multi-module parallel builds
  • No performance impact - same O(n) complexity
  • Backward compatible - no API changes
  • Thread-safe - handles concurrent builds correctly

This fix resolves the issue reported in #10995 where users experienced build failures with IllegalStateException: Recursive update during parallel builds of complex Maven projects.


Pull Request opened by Augment Code with guidance from the PR author

@gnodet gnodet added the bug Something isn't working label Jul 25, 2025
Fixes apache#10995

The SmartProjectComparator.getProjectWeight() method was using
ConcurrentHashMap.computeIfAbsent() in a recursive context, which
could lead to IllegalStateException: Recursive update when calculating
project weights in complex dependency graphs or concurrent scenarios.

Changes:
- Replace computeIfAbsent() with explicit get() + putIfAbsent() pattern
- Eliminate recursive calls to computeIfAbsent() that violate
  ConcurrentHashMap's internal constraints
- Maintain thread safety using putIfAbsent() for concurrent access
- Add comprehensive test case to verify the fix under concurrent load

The fix preserves all existing functionality while eliminating the
recursive update exception that could occur during parallel builds
of large multi-module projects.
@gnodet gnodet force-pushed the fix-recursive-update-issue-10995 branch from 4644977 to f68f1d7 Compare August 6, 2025 08:25
@gnodet gnodet merged commit 84bf592 into apache:master Aug 6, 2025
19 checks passed
@github-actions github-actions Bot added this to the 4.1.0 milestone Aug 6, 2025
@gnodet gnodet deleted the fix-recursive-update-issue-10995 branch September 2, 2025 05:24
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.

Recursive update in SmartProjectComparator

4 participants