Skip to content

Commit b11d040

Browse files
authored
IGNITE-26655 Fix testHandshakesFailedTimeout (#6847)
1 parent 58b8152 commit b11d040

File tree

1 file changed

+79
-6
lines changed

1 file changed

+79
-6
lines changed

modules/client/src/test/java/org/apache/ignite/client/ClientMetricsTest.java

Lines changed: 79 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,13 @@
2626
import static org.junit.jupiter.api.Assertions.assertNotNull;
2727
import static org.junit.jupiter.api.Assertions.assertTrue;
2828

29+
import java.lang.System.Logger;
30+
import java.lang.System.Logger.Level;
2931
import java.lang.management.ManagementFactory;
3032
import java.lang.reflect.Constructor;
3133
import java.util.concurrent.CompletableFuture;
34+
import java.util.concurrent.CompletionException;
35+
import java.util.concurrent.CountDownLatch;
3236
import java.util.concurrent.ForkJoinPool;
3337
import java.util.concurrent.SubmissionPublisher;
3438
import java.util.concurrent.TimeUnit;
@@ -45,20 +49,23 @@
4549
import org.apache.ignite.client.fakes.FakeIgniteQueryProcessor;
4650
import org.apache.ignite.client.fakes.FakeIgniteTables;
4751
import org.apache.ignite.internal.client.ClientMetricSource;
52+
import org.apache.ignite.internal.client.ReliableChannel;
4853
import org.apache.ignite.internal.client.TcpIgniteClient;
4954
import org.apache.ignite.internal.metrics.AbstractMetricSource;
5055
import org.apache.ignite.internal.metrics.MetricSet;
5156
import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
5257
import org.apache.ignite.internal.testframework.IgniteTestUtils;
5358
import org.apache.ignite.internal.testframework.WithSystemProperty;
5459
import org.apache.ignite.lang.ErrorGroups.Sql;
60+
import org.apache.ignite.lang.LoggerFactory;
5561
import org.apache.ignite.table.DataStreamerItem;
5662
import org.apache.ignite.table.Table;
5763
import org.apache.ignite.table.Tuple;
5864
import org.junit.jupiter.api.AfterEach;
5965
import org.junit.jupiter.api.Test;
6066
import org.junit.jupiter.params.ParameterizedTest;
6167
import org.junit.jupiter.params.provider.ValueSource;
68+
import org.mockito.Mockito;
6269

6370
/**
6471
* Tests client-side metrics (see also server-side metrics tests in {@link ServerMetricsTest}).
@@ -143,10 +150,27 @@ public void testHandshakesFailed() {
143150
}
144151

145152
@Test
146-
public void testHandshakesFailedTimeout() throws InterruptedException {
147-
AtomicInteger counter = new AtomicInteger();
153+
public void testHandshakesFailedTimeout() {
154+
// Record handshake timeout logs.
155+
// These logs are sent after the timeout is detected by the timeout task and after the metric manager is updated.
156+
// Therefore it's safe to way for them.
157+
// Checkout: org.apache.ignite.internal.client.TcpClientChannel.handshakeAsync
158+
CountDownLatch latch = new CountDownLatch(1);
159+
HandshakeTimeoutLoggerListener handshakeTimeoutListener = new HandshakeTimeoutLoggerListener(latch);
160+
148161
Function<Integer, Boolean> shouldDropConnection = requestIdx -> false;
149-
Function<Integer, Integer> responseDelay = idx -> counter.incrementAndGet() < 3 ? 600 : 0;
162+
// Blocks until a timeout was observed.
163+
Function<Integer, Integer> responseDelay = idx -> {
164+
try {
165+
latch.await();
166+
} catch (InterruptedException e) {
167+
Thread.currentThread().interrupt();
168+
throw new RuntimeException("Interrupted while waiting for handshake timeout latch", e);
169+
}
170+
171+
return 0;
172+
};
173+
150174
server = new TestServer(
151175
1000,
152176
new FakeIgnite(),
@@ -157,13 +181,14 @@ public void testHandshakesFailedTimeout() throws InterruptedException {
157181
null,
158182
null
159183
);
184+
160185
client = clientBuilder()
161186
.connectTimeout(100)
187+
.loggerFactory(handshakeTimeoutListener)
162188
.build();
163189

164-
assertTrue(
165-
IgniteTestUtils.waitForCondition(() -> metrics().handshakesFailedTimeout() >= 1, 200, 6_000),
166-
() -> "handshakesFailedTimeout: " + metrics().handshakesFailedTimeout());
190+
long numObservedTimeouts = handshakeTimeoutListener.count();
191+
assertThat("handshakesFailedTimeout", metrics().handshakesFailedTimeout(), greaterThanOrEqualTo(numObservedTimeouts));
167192
}
168193

169194
@SuppressWarnings("resource")
@@ -407,4 +432,52 @@ private Builder clientBuilder() {
407432
private ClientMetricSource metrics() {
408433
return ((TcpIgniteClient) client).metrics();
409434
}
435+
436+
/** This logger factory intercepts and counts Handshake Timeout messages. Can be generalised in the future for other messages. */
437+
private static class HandshakeTimeoutLoggerListener implements LoggerFactory {
438+
private final CountDownLatch latch;
439+
440+
private final AtomicInteger counter;
441+
442+
HandshakeTimeoutLoggerListener(CountDownLatch latch) {
443+
this.latch = latch;
444+
this.counter = new AtomicInteger(0);
445+
}
446+
447+
@Override
448+
public Logger forName(String name) {
449+
Logger base = System.getLogger(name);
450+
if (ReliableChannel.class.getName().equals(name)) {
451+
Logger tracker = Mockito.mock(
452+
base.getClass(),
453+
Mockito.withSettings()
454+
.spiedInstance(base)
455+
.defaultAnswer(Mockito.CALLS_REAL_METHODS)
456+
.stubOnly()
457+
);
458+
459+
Mockito.doAnswer(inv -> {
460+
Throwable err = inv.getArgument(2);
461+
if (err instanceof CompletionException && err.getCause() instanceof IgniteClientConnectionException) {
462+
IgniteClientConnectionException ex = (IgniteClientConnectionException) err.getCause();
463+
if (ex.getMessage().startsWith("Handshake timeout")) {
464+
// Updates the timeout counter and releases the responses so that we can connect to the server.
465+
counter.getAndIncrement();
466+
latch.countDown();
467+
}
468+
}
469+
470+
return inv.callRealMethod();
471+
}).when(tracker).log(Mockito.eq(Level.WARNING), Mockito.anyString(), Mockito.any(Throwable.class));
472+
473+
return tracker;
474+
} else {
475+
return base;
476+
}
477+
}
478+
479+
int count() {
480+
return this.counter.get();
481+
}
482+
}
410483
}

0 commit comments

Comments
 (0)