Skip to content

Commit 1f1046d

Browse files
Correct context for ClusterConnManager listener (#83035) (#83184)
* Correct context for ClusterConnManager listener (#83035) Today `ClusterConnectionManager#connectToNode` completes its listeners in the thread context in which the connection completes, which may not be the correct context if there are multiple concurrent connection attempts. With this commit we make sure to complete each listener in the context in which it was passed to the corresponding call to `connectToNode`. Co-authored-by: ievgen.degtiarenko <[email protected]> * Missing import * Fix up tests Co-authored-by: ievgen.degtiarenko <[email protected]>
1 parent 5ad8ea2 commit 1f1046d

File tree

32 files changed

+246
-108
lines changed

32 files changed

+246
-108
lines changed

docs/changelog/83035.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 83035
2+
summary: Correct context for `ClusterConnManager` listener
3+
area: Network
4+
type: bug
5+
issues: []

server/src/main/java/org/elasticsearch/transport/ClusterConnectionManager.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@
1010
import org.apache.logging.log4j.LogManager;
1111
import org.apache.logging.log4j.Logger;
1212
import org.elasticsearch.action.ActionListener;
13+
import org.elasticsearch.action.support.ContextPreservingActionListener;
1314
import org.elasticsearch.cluster.node.DiscoveryNode;
1415
import org.elasticsearch.common.settings.Settings;
1516
import org.elasticsearch.common.util.concurrent.ConcurrentCollections;
1617
import org.elasticsearch.common.util.concurrent.ListenableFuture;
1718
import org.elasticsearch.common.util.concurrent.RunOnce;
19+
import org.elasticsearch.common.util.concurrent.ThreadContext;
1820
import org.elasticsearch.core.AbstractRefCounted;
1921
import org.elasticsearch.core.Nullable;
2022
import org.elasticsearch.core.Releasable;
@@ -44,18 +46,20 @@ public class ClusterConnectionManager implements ConnectionManager {
4446
private final AbstractRefCounted connectingRefCounter = AbstractRefCounted.of(this::pendingConnectionsComplete);
4547

4648
private final Transport transport;
49+
private final ThreadContext threadContext;
4750
private final ConnectionProfile defaultProfile;
4851
private final AtomicBoolean closing = new AtomicBoolean(false);
4952
private final CountDownLatch closeLatch = new CountDownLatch(1);
5053
private final DelegatingNodeConnectionListener connectionListener = new DelegatingNodeConnectionListener();
5154

52-
public ClusterConnectionManager(Settings settings, Transport transport) {
53-
this(ConnectionProfile.buildDefaultConnectionProfile(settings), transport);
55+
public ClusterConnectionManager(Settings settings, Transport transport, ThreadContext threadContext) {
56+
this(ConnectionProfile.buildDefaultConnectionProfile(settings), transport, threadContext);
5457
}
5558

56-
public ClusterConnectionManager(ConnectionProfile connectionProfile, Transport transport) {
59+
public ClusterConnectionManager(ConnectionProfile connectionProfile, Transport transport, ThreadContext threadContext) {
5760
this.transport = transport;
5861
this.defaultProfile = connectionProfile;
62+
this.threadContext = threadContext;
5963
}
6064

6165
@Override
@@ -91,7 +95,13 @@ public void connectToNode(
9195
ConnectionValidator connectionValidator,
9296
ActionListener<Releasable> listener
9397
) throws ConnectTransportException {
94-
connectToNodeOrRetry(node, connectionProfile, connectionValidator, 0, listener);
98+
connectToNodeOrRetry(
99+
node,
100+
connectionProfile,
101+
connectionValidator,
102+
0,
103+
ContextPreservingActionListener.wrapPreservingContext(listener, threadContext)
104+
);
95105
}
96106

97107
/**

server/src/main/java/org/elasticsearch/transport/RemoteClusterConnection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ int getNumNodesConnected() {
197197
}
198198

199199
private static ConnectionManager createConnectionManager(ConnectionProfile connectionProfile, TransportService transportService) {
200-
return new ClusterConnectionManager(connectionProfile, transportService.transport);
200+
return new ClusterConnectionManager(connectionProfile, transportService.transport, transportService.threadPool.getThreadContext());
201201
}
202202

203203
ConnectionManager getConnectionManager() {

server/src/main/java/org/elasticsearch/transport/TransportService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ public TransportService(
211211
localNodeFactory,
212212
clusterSettings,
213213
taskHeaders,
214-
new ClusterConnectionManager(settings, transport)
214+
new ClusterConnectionManager(settings, transport, threadPool.getThreadContext())
215215
);
216216
}
217217

server/src/test/java/org/elasticsearch/action/main/MainActionTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.elasticsearch.rest.RestStatus;
2121
import org.elasticsearch.tasks.Task;
2222
import org.elasticsearch.test.ESTestCase;
23+
import org.elasticsearch.threadpool.ThreadPool;
2324
import org.elasticsearch.transport.Transport;
2425
import org.elasticsearch.transport.TransportService;
2526

@@ -77,7 +78,7 @@ public void testMainActionClusterAvailable() {
7778
TransportService transportService = new TransportService(
7879
Settings.EMPTY,
7980
mock(Transport.class),
80-
null,
81+
mock(ThreadPool.class),
8182
TransportService.NOOP_TRANSPORT_INTERCEPTOR,
8283
x -> null,
8384
null,

server/src/test/java/org/elasticsearch/action/search/MultiSearchActionTookTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ private TransportMultiSearchAction createTransportMultiSearchAction(boolean cont
122122
TransportService transportService = new TransportService(
123123
Settings.EMPTY,
124124
mock(Transport.class),
125-
null,
125+
threadPool,
126126
TransportService.NOOP_TRANSPORT_INTERCEPTOR,
127127
boundAddress -> DiscoveryNode.createLocal(settings, boundAddress.publishAddress(), UUIDs.randomBase64UUID()),
128128
null,

server/src/test/java/org/elasticsearch/action/support/replication/TransportWriteActionTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ protected TestAction(boolean withDocumentFailureOnPrimary, boolean withDocumentF
387387
new TransportService(
388388
Settings.EMPTY,
389389
mock(Transport.class),
390-
null,
390+
TransportWriteActionTests.threadPool,
391391
TransportService.NOOP_TRANSPORT_INTERCEPTOR,
392392
x -> null,
393393
null,

server/src/test/java/org/elasticsearch/cluster/coordination/JoinHelperTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.elasticsearch.test.transport.CapturingTransport;
2727
import org.elasticsearch.test.transport.CapturingTransport.CapturedRequest;
2828
import org.elasticsearch.test.transport.MockTransport;
29+
import org.elasticsearch.threadpool.ThreadPool;
2930
import org.elasticsearch.transport.ClusterConnectionManager;
3031
import org.elasticsearch.transport.RemoteTransportException;
3132
import org.elasticsearch.transport.TransportException;
@@ -53,15 +54,16 @@ public void testJoinDeduplication() {
5354
DeterministicTaskQueue deterministicTaskQueue = new DeterministicTaskQueue();
5455
CapturingTransport capturingTransport = new HandshakingCapturingTransport();
5556
DiscoveryNode localNode = new DiscoveryNode("node0", buildNewFakeTransportAddress(), Version.CURRENT);
57+
final ThreadPool threadPool = deterministicTaskQueue.getThreadPool();
5658
TransportService transportService = new TransportService(
5759
Settings.EMPTY,
5860
capturingTransport,
59-
deterministicTaskQueue.getThreadPool(),
61+
threadPool,
6062
TransportService.NOOP_TRANSPORT_INTERCEPTOR,
6163
x -> localNode,
6264
null,
6365
Collections.emptySet(),
64-
new ClusterConnectionManager(Settings.EMPTY, capturingTransport)
66+
new ClusterConnectionManager(Settings.EMPTY, capturingTransport, threadPool.getThreadContext())
6567
);
6668
JoinHelper joinHelper = new JoinHelper(
6769
Settings.EMPTY,

server/src/test/java/org/elasticsearch/discovery/PeerFinderTests.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.elasticsearch.test.transport.CapturingTransport;
3030
import org.elasticsearch.test.transport.CapturingTransport.CapturedRequest;
3131
import org.elasticsearch.test.transport.StubbableConnectionManager;
32+
import org.elasticsearch.threadpool.ThreadPool;
3233
import org.elasticsearch.transport.ClusterConnectionManager;
3334
import org.elasticsearch.transport.ConnectionManager;
3435
import org.elasticsearch.transport.TransportException;
@@ -210,7 +211,13 @@ public void setup() {
210211

211212
localNode = newDiscoveryNode("local-node");
212213

213-
ConnectionManager innerConnectionManager = new ClusterConnectionManager(settings, capturingTransport);
214+
final ThreadPool threadPool = deterministicTaskQueue.getThreadPool();
215+
216+
final ConnectionManager innerConnectionManager = new ClusterConnectionManager(
217+
settings,
218+
capturingTransport,
219+
threadPool.getThreadContext()
220+
);
214221
StubbableConnectionManager connectionManager = new StubbableConnectionManager(innerConnectionManager);
215222
connectionManager.setDefaultNodeConnectedBehavior((cm, discoveryNode) -> {
216223
final boolean isConnected = connectedNodes.contains(discoveryNode);
@@ -222,7 +229,7 @@ public void setup() {
222229
transportService = new TransportService(
223230
settings,
224231
capturingTransport,
225-
deterministicTaskQueue.getThreadPool(),
232+
threadPool,
226233
TransportService.NOOP_TRANSPORT_INTERCEPTOR,
227234
boundTransportAddress -> localNode,
228235
null,

server/src/test/java/org/elasticsearch/transport/ClusterConnectionManagerTests.java

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.common.logging.Loggers;
2020
import org.elasticsearch.common.settings.Settings;
2121
import org.elasticsearch.common.transport.TransportAddress;
22+
import org.elasticsearch.common.util.concurrent.ThreadContext;
2223
import org.elasticsearch.core.Releasable;
2324
import org.elasticsearch.core.Releasables;
2425
import org.elasticsearch.core.TimeValue;
@@ -45,6 +46,7 @@
4546
import java.util.function.Supplier;
4647

4748
import static org.elasticsearch.test.ActionListenerUtils.anyActionListener;
49+
import static org.hamcrest.Matchers.equalTo;
4850
import static org.hamcrest.Matchers.notNullValue;
4951
import static org.mockito.ArgumentMatchers.any;
5052
import static org.mockito.ArgumentMatchers.eq;
@@ -63,7 +65,7 @@ public void createConnectionManager() {
6365
Settings settings = Settings.builder().put("node.name", ClusterConnectionManagerTests.class.getSimpleName()).build();
6466
threadPool = new ThreadPool(settings);
6567
transport = mock(Transport.class);
66-
connectionManager = new ClusterConnectionManager(settings, transport);
68+
connectionManager = new ClusterConnectionManager(settings, transport, threadPool.getThreadContext());
6769
TimeValue oneSecond = new TimeValue(1000);
6870
TimeValue oneMinute = TimeValue.timeValueMinutes(1);
6971
connectionProfile = ConnectionProfile.buildSingleChannelProfile(
@@ -254,6 +256,9 @@ public void testConcurrentConnects() throws Exception {
254256
int threadCount = between(1, 10);
255257
Releasable[] releasables = new Releasable[threadCount];
256258

259+
final ThreadContext threadContext = threadPool.getThreadContext();
260+
final String contextHeader = "test-context-header";
261+
257262
CyclicBarrier barrier = new CyclicBarrier(threadCount + 1);
258263
Semaphore pendingCloses = new Semaphore(threadCount);
259264
for (int i = 0; i < threadCount; i++) {
@@ -265,27 +270,33 @@ public void testConcurrentConnects() throws Exception {
265270
throw new RuntimeException(e);
266271
}
267272
CountDownLatch latch = new CountDownLatch(1);
268-
connectionManager.connectToNode(node, connectionProfile, validator, ActionListener.wrap(c -> {
269-
assert connectionManager.nodeConnected(node);
270-
271-
assertTrue(pendingCloses.tryAcquire());
272-
connectionManager.getConnection(node).addRemovedListener(ActionListener.wrap(pendingCloses::release));
273-
274-
if (randomBoolean()) {
275-
releasables[threadIndex] = c;
276-
nodeConnectedCount.incrementAndGet();
277-
} else {
278-
Releasables.close(c);
279-
nodeClosedCount.incrementAndGet();
280-
}
281-
282-
assert latch.getCount() == 1;
283-
latch.countDown();
284-
}, e -> {
285-
nodeFailureCount.incrementAndGet();
286-
assert latch.getCount() == 1;
287-
latch.countDown();
288-
}));
273+
try (ThreadContext.StoredContext ignored = threadContext.stashContext()) {
274+
final String contextValue = randomAlphaOfLength(10);
275+
threadContext.putHeader(contextHeader, contextValue);
276+
connectionManager.connectToNode(node, connectionProfile, validator, ActionListener.wrap(c -> {
277+
assert connectionManager.nodeConnected(node);
278+
assertThat(threadContext.getHeader(contextHeader), equalTo(contextValue));
279+
280+
assertTrue(pendingCloses.tryAcquire());
281+
connectionManager.getConnection(node).addRemovedListener(ActionListener.wrap(pendingCloses::release));
282+
283+
if (randomBoolean()) {
284+
releasables[threadIndex] = c;
285+
nodeConnectedCount.incrementAndGet();
286+
} else {
287+
Releasables.close(c);
288+
nodeClosedCount.incrementAndGet();
289+
}
290+
291+
assert latch.getCount() == 1;
292+
latch.countDown();
293+
}, e -> {
294+
assertThat(threadContext.getHeader(contextHeader), equalTo(contextValue));
295+
nodeFailureCount.incrementAndGet();
296+
assert latch.getCount() == 1;
297+
latch.countDown();
298+
}));
299+
}
289300
try {
290301
latch.await();
291302
} catch (InterruptedException e) {

0 commit comments

Comments
 (0)