Skip to content

Fix thread blocking in NotificationControllerV2 by replacing synchronized Multimap with case-insensitive concurrent implementation#5451

Closed
Copilot wants to merge 8 commits into
masterfrom
copilot/fix-5450
Closed

Fix thread blocking in NotificationControllerV2 by replacing synchronized Multimap with case-insensitive concurrent implementation#5451
Copilot wants to merge 8 commits into
masterfrom
copilot/fix-5450

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Aug 24, 2025

  • Identify race condition in CaseInsensitiveConcurrentMultimap
  • Fix put() method to use compute() for atomic operations
  • Fix remove() method to use computeIfPresent() for atomic operations
  • Add comprehensive test for concurrent put/remove race condition
  • Verify implementation correctness

💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Aug 24, 2025

@nobodyiam 👋 This repository doesn't have Copilot instructions. With Copilot instructions, I can understand the repository better, work faster and produce higher quality PRs.

I can generate a .github/copilot-instructions.md file for you automatically. Click here to open a pre-filled issue and assign it to me. I'll write the instructions, and then tag you for review.

Copilot AI changed the title [WIP] 状态检测接口出现偶发的线程阻塞问题 Fix thread blocking in NotificationControllerV2 by replacing synchronized Multimap with ConcurrentHashMap Aug 24, 2025
Copilot AI requested a review from nobodyiam August 24, 2025 06:06
@nobodyiam
Copy link
Copy Markdown
Member

@copilot the build failed with the error messages

Error:  Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:compile (default-compile) on project apollo-configservice: Compilation failure: Compilation failure: 
Error:  /home/runner/work/apollo/apollo/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/NotificationControllerV2.java:[225,71] cannot find symbol
Error:    symbol:   class Multimap
Error:    location: class com.ctrip.framework.apollo.configservice.controller.NotificationControllerV2
Error:  /home/runner/work/apollo/apollo/apollo-configservice/src/main/java/com/ctrip/framework/apollo/configservice/controller/NotificationControllerV2.java:[142,5] cannot find symbol
Error:    symbol:   class Multimap
Error:    location: class com.ctrip.framework.apollo.configservice.controller.NotificationControllerV2
Error:  -> [Help 1]
Error:  
Error:  To see the full stack trace of the errors, re-run Maven with the -e switch.
Error:  Re-run Maven using the -X switch to enable full debug logging.
Error:  
Error:  For more information about the errors and possible solutions, please read the following articles:
Error:  [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
Error:  
Error:  After correcting the problems, you can resume the build with the command
Error:    mvn <args> -rf :apollo-configservice
Error: Process completed with exit code 1.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Aug 24, 2025

@copilot the build failed with the error messages

...

Fixed the compilation errors by adding back the missing com.google.common.collect.Multimap import. The issue occurred because when I replaced the synchronized Multimap field with ConcurrentMultimap, I inadvertently removed the Guava Multimap import that was still needed for method parameters and variables that use the return type from WatchKeysUtil.assembleAllWatchKeys().

Commit: e49746c

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • central.sonatype.com

If you need me to access, download, or install something from one of these locations, you can either:

@nobodyiam
Copy link
Copy Markdown
Member

@copilot

  1. Should ConcurrentMultimap implement com.google.common.collect.Multimap?
  2. Should ConcurrentMultimap be renamed to CaseInsensitiveConcurrentMultimap?
  3. The original version used Ordering.natural()—is that still necessary?

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Aug 24, 2025

@copilot

  1. Should ConcurrentMultimap implement com.google.common.collect.Multimap?
    ...

I've addressed all three questions:

  1. Should ConcurrentMultimap implement com.google.common.collect.Multimap?Yes - I've implemented the full Multimap interface to make it a true drop-in replacement. This ensures API compatibility and eliminates the need for separate type handling.

  2. Should ConcurrentMultimap be renamed to CaseInsensitiveConcurrentMultimap?Yes - Renamed to better reflect the case-insensitive behavior, which is a key differentiating feature from a generic concurrent multimap.

  3. The original version used Ordering.natural()—is that still necessary?No - The ordering was for DeferredResultWrapper values but analysis shows it's not functionally required. The code converts to ArrayList anyway (Lists.newArrayList(deferredResults.get(content))) and doesn't rely on value ordering. Removed ordering for better performance while maintaining all functionality.

The implementation now fully implements the Guava Multimap interface with comprehensive methods like keys(), keySet(), entries(), asMap(), etc., while preserving the case-insensitive behavior and thread safety improvements.

Commit: 1e63197

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • central.sonatype.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.11/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.11/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.11 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.11/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/apollo/apollo org.codehaus.plexus.classworlds.launcher.Launcher dependency:build-classpath -q -Dmdep.outputFile=/dev/stdout (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.11/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.11/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.11 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.11/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/apollo/apollo org.codehaus.plexus.classworlds.launcher.Launcher compile -pl apollo-configservice -am -DskipTests=true -q (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI changed the title Fix thread blocking in NotificationControllerV2 by replacing synchronized Multimap with ConcurrentHashMap Fix thread blocking in NotificationControllerV2 by replacing synchronized Multimap with case-insensitive concurrent implementation Aug 24, 2025
Copy link
Copy Markdown
Member

@nobodyiam nobodyiam left a comment

Choose a reason for hiding this comment

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

We could define the variable using the Multimap interface. See comments below.
Also, please update CHANGES.md to reflect this PR and fix the compilation issue:

Error:  Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:testCompile (default-testCompile) on project apollo-configservice: Compilation failure: Compilation failure: 
Error:  /home/runner/work/apollo/apollo/apollo-configservice/src/test/java/com/ctrip/framework/apollo/configservice/controller/NotificationControllerV2Test.java:[83,11] cannot find symbol
Error:    symbol:   class ConcurrentMultimap
Error:    location: class com.ctrip.framework.apollo.configservice.controller.NotificationControllerV2Test
Error:  /home/runner/work/apollo/apollo/apollo-configservice/src/test/java/com/ctrip/framework/apollo/configservice/controller/NotificationControllerV2Test.java:[110,10] cannot find symbol
Error:    symbol:   class ConcurrentMultimap
Error:    location: class com.ctrip.framework.apollo.configservice.controller.NotificationControllerV2Test
Error:  -> [Help 1]
Error:  
Error:  To see the full stack trace of the errors, re-run Maven with the -e switch.
Error:  Re-run Maven using the -X switch to enable full debug logging.
Error:  
Error:  For more information about the errors and possible solutions, please read the following articles:
Error:  [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
Error:  
Error:  After correcting the problems, you can resume the build with the command
Error:    mvn <args> -rf :apollo-configservice
Error: Process completed with exit code 1.

private static final Logger logger = LoggerFactory.getLogger(NotificationControllerV2.class);
private final Multimap<String, DeferredResultWrapper> deferredResults =
Multimaps.synchronizedSetMultimap(TreeMultimap.create(String.CASE_INSENSITIVE_ORDER, Ordering.natural()));
private final CaseInsensitiveConcurrentMultimap<String, DeferredResultWrapper> deferredResults = new CaseInsensitiveConcurrentMultimap<>();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
private final CaseInsensitiveConcurrentMultimap<String, DeferredResultWrapper> deferredResults = new CaseInsensitiveConcurrentMultimap<>();
private final Multimap<String, DeferredResultWrapper> deferredResults =
new CaseInsensitiveConcurrentMultimap<>();

private Gson gson;

private Multimap<String, DeferredResultWrapper> deferredResults;
private ConcurrentMultimap<String, DeferredResultWrapper> deferredResults;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
private ConcurrentMultimap<String, DeferredResultWrapper> deferredResults;
private Multimap<String, DeferredResultWrapper> deferredResults;


deferredResults =
(Multimap<String, DeferredResultWrapper>) ReflectionTestUtils.getField(controller, "deferredResults");
(ConcurrentMultimap<String, DeferredResultWrapper>) ReflectionTestUtils.getField(controller, "deferredResults");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
(ConcurrentMultimap<String, DeferredResultWrapper>) ReflectionTestUtils.getField(controller, "deferredResults");
(Multimap<String, DeferredResultWrapper>) ReflectionTestUtils.getField(controller, "deferredResults");

@nobodyiam
Copy link
Copy Markdown
Member

@copilot Please see the review comments above.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Aug 24, 2025

We could define the variable using the Multimap interface. See comments below.
Also, please update CHANGES.md to reflect this PR and fix the compilation issue:

...

I've addressed all the requested changes:

  1. Changed field declaration to use Multimap interface - Updated the deferredResults field to use Multimap<String, DeferredResultWrapper> instead of the concrete implementation
  2. Fixed test compilation errors - Updated test file variable declarations to use Multimap interface and removed unnecessary imports
  3. Updated CHANGES.md - Added entry documenting the thread blocking fix

All compilation issues should now be resolved while maintaining the API compatibility benefits of using the interface type.

Commit: a3976db

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • central.sonatype.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.11/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.11/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.11 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.11/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/apollo/apollo org.codehaus.plexus.classworlds.launcher.Launcher compile -pl apollo-configservice -am -DskipTests=true -q (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.11/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.11/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.11 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.11/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/apollo/apollo org.codehaus.plexus.classworlds.launcher.Launcher test-compile -DskipTests=true -q (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Copilot AI requested a review from nobodyiam August 24, 2025 12:01
Comment thread CHANGES.md Outdated
@nobodyiam nobodyiam marked this pull request as ready for review August 25, 2025 01:18
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Aug 25, 2025

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.


Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot dosubot Bot added the size:XL This PR changes 500-999 lines, ignoring generated files. label Aug 25, 2025
Copilot AI requested a review from nobodyiam August 25, 2025 01:20
@nobodyiam
Copy link
Copy Markdown
Member

@copilot There appears to be a concurrency issue in CaseInsensitiveConcurrentMultimap:

  1. Thread A checks at line 83 and sees the values set is empty.
  2. Thread B retrieves the values set at line 60.
  3. Thread A removes the values set from the map at line 84.
  4. Thread B adds a value to the now-removed set at line 61.

As a result, the value added by Thread B is lost.

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Jan 17, 2026

@copilot There appears to be a concurrency issue in CaseInsensitiveConcurrentMultimap:

  1. Thread A checks at line 83 and sees the values set is empty.
    ...

Fixed the race condition by replacing computeIfAbsent() and manual cleanup with atomic compute() and computeIfPresent() operations. This ensures the entire read-modify-write sequence is atomic and prevents values from being added to sets that are being removed from the map.

The fix ensures:

  • put() uses compute() to atomically create/retrieve the set and add the value
  • remove() uses computeIfPresent() to atomically remove the value and clean up empty sets by returning null

Added a specific test (testConcurrentPutAndRemoveRaceCondition) that targets this exact scenario with concurrent threads performing puts and removes.

Commit: 6985cb5

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • central.sonatype.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/apollo/apollo org.codehaus.plexus.classworlds.launcher.Launcher test -Dtest=CaseInsensitiveConcurrentMultimapTest -pl apollo-configservice (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/apollo/apollo org.codehaus.plexus.classworlds.launcher.Launcher clean install -DskipTests -pl apollo-configservice -am (dns block)
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --enable-native-access=ALL-UNNAMED -classpath /usr/share/apache-maven-3.9.12/boot/plexus-classworlds-2.9.0.jar -Dclassworlds.conf=/usr/share/apache-maven-3.9.12/bin/m2.conf -Dmaven.home=/usr/share/apache-maven-3.9.12 -Dlibrary.jansi.path=/usr/share/apache-maven-3.9.12/lib/jansi-native -Dmaven.multiModuleProjectDirectory=/home/REDACTED/work/apollo/apollo org.codehaus.plexus.classworlds.launcher.Launcher clean install -DskipTests (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

@nobodyiam
Copy link
Copy Markdown
Member

The full implementation of Multimap is not necessary and is thus complicated, use #5532 instead

@nobodyiam nobodyiam closed this Jan 17, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jan 17, 2026
@nobodyiam nobodyiam deleted the copilot/fix-5450 branch February 25, 2026 12:24
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

size:XL This PR changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants