Skip to content

fix: make upstream lists thread-safe (#6857) - #7062

Open
juicewcode wants to merge 8 commits into
apache:masterfrom
juicewcode:fix/6857-upstream-list-thread-safety
Open

juicewcode wants to merge 8 commits into
apache:masterfrom
juicewcode:fix/6857-upstream-list-thread-safety

Conversation

@juicewcode

Copy link
Copy Markdown
Contributor

Fixes #6857

Summary

  • Replace mutable upstream ArrayList instances with CopyOnWriteArrayList.
  • Prevent request threads from observing unsafe concurrent modifications made by health-check threads.
  • Preserve the existing synchronization for compound upstream update operations.

Test

  • Add a regression test verifying that upstream lists use CopyOnWriteArrayList.
  • Verify that an existing iterator retains a stable snapshot when an upstream is added.
  • Verify that subsequent reads observe the newly added upstream.

Make sure that:

  • You have read the contribution guidelines.
  • You submit test cases (unit or integration tests) that back your changes.
  • Your local test passed ./mvnw clean install -Dmaven.javadoc.skip=true.

  Use CopyOnWriteArrayList for healthy and unhealthy upstream collections so request threads can safely iterate while
  health-check threads update them.

  Add a regression test covering snapshot iteration and visibility of subsequent upstream updates.

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

One or more issues must be addressed before approval.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR improves concurrent access to gateway upstream lists during health checks.

Changes:

  • Uses CopyOnWriteArrayList for upstream map values.
  • Adds iterator snapshot and update visibility regression coverage.
File summaries
File Description
shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java Updated as part of this pull request.
shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/cache/UpstreamCheckTask.java Updated as part of this pull request.
Review details

Suppressed comments (1)

shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/cache/UpstreamCacheManagerTest.java:89

  • This fixture leaves health checking enabled (the Upstream builder defaults it to true) while the singleton manager starts a scheduled health-check thread. The synthetic copy-on-write-url:8080 is not a reachable endpoint, so that thread can mark it unhealthy and remove it between submit and the size assertion, making this regression test flaky. Disable health checking for this fixture so only the explicit triggerAddOne changes the list.
        final Upstream upstream = Upstream.builder().url("copy-on-write-url:8080").status(true).build();
  • Files reviewed: 2/2 changed files
  • Comments generated: 1
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +290 to 292
List<Upstream> list = MapUtils.computeIfAbsent(map, selectorId, k -> new CopyOnWriteArrayList<>());
if (!list.contains(upstream)) {
list.add(upstream);
  Return a per-request snapshot from findUpstreamListBySelectorId so index-based load balancers observe a stable list while
  health-check updates are in progress.

  Update the regression test to verify that existing snapshots remain unchanged and subsequent requests observe newly added
  upstreams.

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 does not verify the internal copy-on-write collection or iterator guarantee described by the PR.

Get a fresh assessment by requesting another Copilot review.

Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 1
  • Review effort level: Balanced

Comment on lines +92 to +98
List<Upstream> snapshot = upstreamCacheManager.findUpstreamListBySelectorId(selectorId);
Upstream added = Upstream.builder().url("added-url:8080").status(true).build();
getUpstreamCheckTask(upstreamCacheManager).triggerAddOne(selectorId, added);

Assertions.assertEquals(1, snapshot.size());
Assertions.assertSame(upstream, snapshot.get(0));
Assertions.assertEquals(2, upstreamCacheManager.findUpstreamListBySelectorId(selectorId).size());

@Aias00 Aias00 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.

Approved as PMC (Aias00). Small, well-scoped fix with regression tests; green CI, mergeable. Reviewed the diff.

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.

[BUG] UpstreamCacheManager.findUpstreamListBySelectorId returns live ArrayList mutated by health-check thread without reader sync

3 participants