Skip to content

Commit eaca16c

Browse files
dkasimovskiyclaude
andcommitted
refactor(tests): dedupe HashedWheelTimer shutdown hook and fix regressions
Extract the four identical inline shutdown hooks (one per test class) into a single TimerShutdownHook helper. Two copies of the helper live in tarantool-core and tarantool-schema test sources; client and jackson reuse the core copy via a new test-jar dependency on tarantool-core. schema does not depend on core so the helper is duplicated there rather than introducing a new cross-module dependency. Also revert a dead `version == null` ternary branch in IProtoClientWatchersTest.checkTTVersion (the null branch returned a value that never changed the assertion outcome) and rename testTimeoutCancel to testLocalTimerHasNoPending while inlining the redundant localFactory variable, so the test name describes what it actually does after the static-timer refactor in f2d5c4d. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent f2d5c4d commit eaca16c

10 files changed

Lines changed: 105 additions & 90 deletions

File tree

tarantool-client/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@
2525
<groupId>io.tarantool</groupId>
2626
<artifactId>tarantool-core</artifactId>
2727
</dependency>
28+
<dependency>
29+
<groupId>io.tarantool</groupId>
30+
<artifactId>tarantool-core</artifactId>
31+
<version>${project.version}</version>
32+
<type>test-jar</type>
33+
<scope>test</scope>
34+
</dependency>
2835
<dependency>
2936
<groupId>io.tarantool</groupId>
3037
<artifactId>tarantool-jackson-mapping</artifactId>

tarantool-client/src/test/java/io/tarantool/client/integration/TarantoolCrudClientWithRetryTest.java

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import java.time.Duration;
99
import java.util.Collections;
10-
import java.util.Set;
1110
import java.util.concurrent.CompletableFuture;
1211
import java.util.concurrent.CompletionException;
1312
import java.util.concurrent.TimeUnit;
@@ -34,31 +33,13 @@
3433
import io.tarantool.client.crud.TarantoolCrudSpace;
3534
import io.tarantool.client.factory.TarantoolFactory;
3635
import io.tarantool.core.exceptions.BoxError;
36+
import io.tarantool.core.integration.TimerShutdownHook;
3737
import io.tarantool.mapping.Tuple;
3838

3939
@Timeout(value = 5)
4040
@Testcontainers
4141
public class TarantoolCrudClientWithRetryTest {
4242

43-
private static final boolean SHUTDOWN_HOOK_REGISTERED = registerShutdownHook();
44-
45-
private static boolean registerShutdownHook() {
46-
Runtime.getRuntime()
47-
.addShutdownHook(
48-
new Thread(
49-
() -> {
50-
Timer t = timerService;
51-
if (t != null) {
52-
Set<io.netty.util.Timeout> pending = t.stop();
53-
if (pending != null && !pending.isEmpty()) {
54-
pending.forEach(io.netty.util.Timeout::cancel);
55-
}
56-
}
57-
},
58-
"tarantool-crud-client-retry-test-timer-shutdown"));
59-
return true;
60-
}
61-
6243
public class OperationsRepeater<T> {
6344

6445
private final Supplier<CompletableFuture<T>> operation;
@@ -119,6 +100,11 @@ private void execute() {
119100
private static final Person personInstance = new Person(1, true, "Roman");
120101
private static final Timer timerService = new HashedWheelTimer();
121102

103+
static {
104+
TimerShutdownHook.register(
105+
() -> timerService, "tarantool-crud-client-retry-test-timer-shutdown");
106+
}
107+
122108
@BeforeAll
123109
public static void setUp() throws Exception {
124110
if (isCartridgeAvailable()) {

tarantool-core/src/test/java/io/tarantool/core/integration/BaseTest.java

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,13 @@
1111
import java.util.HashMap;
1212
import java.util.List;
1313
import java.util.Map;
14-
import java.util.Set;
1514

1615
import io.netty.bootstrap.Bootstrap;
1716
import io.netty.channel.ChannelOption;
1817
import io.netty.channel.MultiThreadIoEventLoopGroup;
1918
import io.netty.channel.nio.NioIoHandler;
2019
import io.netty.channel.socket.nio.NioSocketChannel;
2120
import io.netty.util.HashedWheelTimer;
22-
import io.netty.util.Timeout;
2321
import io.netty.util.Timer;
2422
import org.msgpack.core.MessagePack;
2523
import org.msgpack.core.MessageUnpacker;
@@ -34,25 +32,6 @@
3432

3533
public abstract class BaseTest {
3634

37-
private static final boolean SHUTDOWN_HOOK_REGISTERED = registerShutdownHook();
38-
39-
private static boolean registerShutdownHook() {
40-
Runtime.getRuntime()
41-
.addShutdownHook(
42-
new Thread(
43-
() -> {
44-
Timer t = timerService;
45-
if (t != null) {
46-
Set<Timeout> pending = t.stop();
47-
if (pending != null && !pending.isEmpty()) {
48-
pending.forEach(Timeout::cancel);
49-
}
50-
}
51-
},
52-
"base-test-timer-shutdown"));
53-
return true;
54-
}
55-
5635
protected static final Bootstrap bootstrap =
5736
new Bootstrap()
5837
.group(new MultiThreadIoEventLoopGroup(1, NioIoHandler.newFactory()))
@@ -83,6 +62,10 @@ private static boolean registerShutdownHook() {
8362

8463
protected static ConnectionFactory factory = new ConnectionFactory(bootstrap, timerService);
8564

65+
static {
66+
TimerShutdownHook.register(() -> timerService, "base-test-timer-shutdown");
67+
}
68+
8669
protected static ArrayValue decodeTuple(IProtoClient client, ArrayValue arrayValue)
8770
throws IOException {
8871
if (client.hasTupleExtension()) {

tarantool-core/src/test/java/io/tarantool/core/integration/IProtoClientTest.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static org.junit.jupiter.api.Assertions.assertThrows;
2929
import static org.junit.jupiter.api.Assertions.assertTrue;
3030
import io.netty.util.HashedWheelTimer;
31+
import io.netty.util.Timer;
3132
import org.junit.jupiter.api.AfterAll;
3233
import org.junit.jupiter.api.Assertions;
3334
import org.junit.jupiter.api.BeforeAll;
@@ -1515,19 +1516,23 @@ public void testPing() throws Exception {
15151516
}
15161517

15171518
@Test
1518-
public void testTimeoutCancel() throws Exception {
1519-
io.netty.util.Timer localTimer = new HashedWheelTimer();
1520-
ConnectionFactory localFactory = new ConnectionFactory(bootstrap, localTimer);
1519+
public void testLocalTimerHasNoPending() throws Exception {
1520+
Timer localTimer = new HashedWheelTimer();
15211521
IProtoClient client =
1522-
new IProtoClientImpl(localFactory, localTimer, DEFAULT_WATCHER_OPTS, null, null, true);
1522+
new IProtoClientImpl(
1523+
new ConnectionFactory(bootstrap, localTimer),
1524+
localTimer,
1525+
DEFAULT_WATCHER_OPTS,
1526+
null,
1527+
null,
1528+
true);
15231529
client.connect(address, 3_000).get();
15241530
client.authorize(API_USER, CREDS.get(API_USER)).join();
15251531
IProtoMessage message = client.ping().get();
15261532
checkMessageHeader(message, IPROTO_OK, 4);
15271533
assertEquals(0, message.getBody().map().size());
15281534

1529-
Set<io.netty.util.Timeout> timers = localTimer.stop();
1530-
assertTrue(timers.isEmpty());
1535+
assertTrue(localTimer.stop().isEmpty());
15311536
}
15321537

15331538
@Test

tarantool-core/src/test/java/io/tarantool/core/integration/IProtoClientWatchersTest.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,6 @@ private void testWatcherRecoveryAfterReconnectOnContainer(
207207
private void checkTTVersion(TarantoolContainer<?> tt, String version) throws Exception {
208208
List<?> result = TarantoolContainerClientHelper.executeCommandDecoded(tt, "return _TARANTOOL");
209209
String ttVersion = (String) result.get(0);
210-
String expectedPrefix = version == null ? ttVersion.split("-")[0] : version.split("-")[0];
211-
assertTrue(ttVersion.startsWith(expectedPrefix));
210+
assertTrue(ttVersion.startsWith(version.split("-")[0]));
212211
}
213212
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2026 VK DIGITAL TECHNOLOGIES LIMITED LIABILITY COMPANY
3+
* All Rights Reserved.
4+
*/
5+
6+
package io.tarantool.core.integration;
7+
8+
import java.util.Set;
9+
import java.util.function.Supplier;
10+
11+
import io.netty.util.Timeout;
12+
import io.netty.util.Timer;
13+
14+
public final class TimerShutdownHook {
15+
private TimerShutdownHook() {}
16+
17+
public static void register(Supplier<Timer> timerRef, String name) {
18+
Runtime.getRuntime()
19+
.addShutdownHook(
20+
new Thread(
21+
() -> {
22+
Timer t = timerRef.get();
23+
if (t != null) {
24+
Set<Timeout> pending = t.stop();
25+
if (pending != null) pending.forEach(Timeout::cancel);
26+
}
27+
},
28+
name));
29+
}
30+
}

tarantool-jackson-mapping/pom.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,13 @@
2525
<groupId>io.tarantool</groupId>
2626
<artifactId>tarantool-core</artifactId>
2727
</dependency>
28+
<dependency>
29+
<groupId>io.tarantool</groupId>
30+
<artifactId>tarantool-core</artifactId>
31+
<version>${project.version}</version>
32+
<type>test-jar</type>
33+
<scope>test</scope>
34+
</dependency>
2835
<dependency>
2936
<groupId>org.msgpack</groupId>
3037
<artifactId>jackson-dataformat-msgpack</artifactId>

tarantool-jackson-mapping/src/test/java/io/tarantool/mapping/integration/BaseTest.java

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,40 +7,20 @@
77

88
import java.util.HashMap;
99
import java.util.Map;
10-
import java.util.Set;
1110

1211
import io.netty.bootstrap.Bootstrap;
1312
import io.netty.channel.ChannelOption;
1413
import io.netty.channel.MultiThreadIoEventLoopGroup;
1514
import io.netty.channel.nio.NioIoHandler;
1615
import io.netty.channel.socket.nio.NioSocketChannel;
1716
import io.netty.util.HashedWheelTimer;
18-
import io.netty.util.Timeout;
1917
import io.netty.util.Timer;
2018

2119
import io.tarantool.core.connection.ConnectionFactory;
20+
import io.tarantool.core.integration.TimerShutdownHook;
2221

2322
public abstract class BaseTest {
2423

25-
private static final boolean SHUTDOWN_HOOK_REGISTERED = registerShutdownHook();
26-
27-
private static boolean registerShutdownHook() {
28-
Runtime.getRuntime()
29-
.addShutdownHook(
30-
new Thread(
31-
() -> {
32-
Timer t = timerService;
33-
if (t != null) {
34-
Set<Timeout> pending = t.stop();
35-
if (pending != null && !pending.isEmpty()) {
36-
pending.forEach(Timeout::cancel);
37-
}
38-
}
39-
},
40-
"jackson-mapping-base-test-timer-shutdown"));
41-
return true;
42-
}
43-
4424
protected static final String API_USER = "api_user";
4525

4626
protected static final Map<String, String> CREDS =
@@ -68,4 +48,8 @@ private static boolean registerShutdownHook() {
6848
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000);
6949
protected static final Timer timerService = new HashedWheelTimer();
7050
protected static final ConnectionFactory factory = new ConnectionFactory(bootstrap, timerService);
51+
52+
static {
53+
TimerShutdownHook.register(() -> timerService, "jackson-mapping-base-test-timer-shutdown");
54+
}
7155
}

tarantool-schema/src/test/java/io/tarantool/client/integration/TarantoolSchemaFetcherTest.java

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
import java.util.HashSet;
1414
import java.util.List;
1515
import java.util.Map;
16-
import java.util.Set;
1716

1817
import static org.junit.jupiter.api.Assertions.assertEquals;
1918
import io.netty.bootstrap.Bootstrap;
@@ -48,25 +47,6 @@
4847
@Timeout(value = 5)
4948
public class TarantoolSchemaFetcherTest {
5049

51-
private static final boolean SHUTDOWN_HOOK_REGISTERED = registerShutdownHook();
52-
53-
private static boolean registerShutdownHook() {
54-
Runtime.getRuntime()
55-
.addShutdownHook(
56-
new Thread(
57-
() -> {
58-
Timer t = timerService;
59-
if (t != null) {
60-
Set<io.netty.util.Timeout> pending = t.stop();
61-
if (pending != null && !pending.isEmpty()) {
62-
pending.forEach(io.netty.util.Timeout::cancel);
63-
}
64-
}
65-
},
66-
"tarantool-schema-fetcher-test-timer-shutdown"));
67-
return true;
68-
}
69-
7050
private static final String API_USER = "api_user";
7151

7252
private static final Map<String, String> CREDS =
@@ -95,6 +75,10 @@ private static boolean registerShutdownHook() {
9575
private static final Timer timerService = new HashedWheelTimer();
9676
private static final ConnectionFactory factory = new ConnectionFactory(bootstrap, timerService);
9777

78+
static {
79+
TimerShutdownHook.register(() -> timerService, "tarantool-schema-fetcher-test-timer-shutdown");
80+
}
81+
9882
private static TarantoolContainer<?> tt;
9983

10084
private Long spacePersonId;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (c) 2026 VK DIGITAL TECHNOLOGIES LIMITED LIABILITY COMPANY
3+
* All Rights Reserved.
4+
*/
5+
6+
package io.tarantool.client.integration;
7+
8+
import java.util.Set;
9+
import java.util.function.Supplier;
10+
11+
import io.netty.util.Timeout;
12+
import io.netty.util.Timer;
13+
14+
public final class TimerShutdownHook {
15+
private TimerShutdownHook() {}
16+
17+
public static void register(Supplier<Timer> timerRef, String name) {
18+
Runtime.getRuntime()
19+
.addShutdownHook(
20+
new Thread(
21+
() -> {
22+
Timer t = timerRef.get();
23+
if (t != null) {
24+
Set<Timeout> pending = t.stop();
25+
if (pending != null) pending.forEach(Timeout::cancel);
26+
}
27+
},
28+
name));
29+
}
30+
}

0 commit comments

Comments
 (0)