Skip to content

Commit 980c707

Browse files
authored
fix(framework): fix SolidityNode shutdown and improve test quality (tronprotocol#6655)
1 parent 6f63a8c commit 980c707

63 files changed

Lines changed: 1067 additions & 1409 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

framework/src/main/java/org/tron/program/SolidityNode.java

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@
22

33
import static org.tron.core.config.Parameter.ChainConstant.BLOCK_PRODUCED_INTERVAL;
44

5+
import com.google.common.annotations.VisibleForTesting;
6+
import java.util.concurrent.ExecutorService;
57
import java.util.concurrent.LinkedBlockingDeque;
8+
import java.util.concurrent.TimeUnit;
69
import java.util.concurrent.atomic.AtomicLong;
710
import lombok.extern.slf4j.Slf4j;
811
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
@@ -11,6 +14,7 @@
1114
import org.tron.common.application.ApplicationFactory;
1215
import org.tron.common.application.TronApplicationContext;
1316
import org.tron.common.client.DatabaseGrpcClient;
17+
import org.tron.common.es.ExecutorServiceManager;
1418
import org.tron.common.parameter.CommonParameter;
1519
import org.tron.common.prometheus.Metrics;
1620
import org.tron.core.ChainBaseManager;
@@ -39,6 +43,9 @@ public class SolidityNode {
3943

4044
private volatile boolean flag = true;
4145

46+
private ExecutorService getBlockEs;
47+
private ExecutorService processBlockEs;
48+
4249
public SolidityNode(Manager dbManager) {
4350
this.dbManager = dbManager;
4451
this.chainBaseManager = dbManager.getChainBaseManager();
@@ -72,13 +79,26 @@ public static void start() {
7279
appT.startup();
7380
SolidityNode node = new SolidityNode(appT.getDbManager());
7481
node.run();
75-
appT.blockUntilShutdown();
82+
awaitShutdown(appT, node);
83+
}
84+
85+
@VisibleForTesting
86+
static void awaitShutdown(Application appT, SolidityNode node) {
87+
try {
88+
appT.blockUntilShutdown();
89+
} finally {
90+
// SolidityNode is created manually rather than managed by Spring/Application,
91+
// so its executors must be shut down explicitly on exit.
92+
node.shutdown();
93+
}
7694
}
7795

7896
private void run() {
7997
try {
80-
new Thread(this::getBlock).start();
81-
new Thread(this::processBlock).start();
98+
getBlockEs = ExecutorServiceManager.newSingleThreadExecutor("solid-get-block");
99+
processBlockEs = ExecutorServiceManager.newSingleThreadExecutor("solid-process-block");
100+
getBlockEs.execute(this::getBlock);
101+
processBlockEs.execute(this::processBlock);
82102
logger.info("Success to start solid node, ID: {}, remoteBlockNum: {}.", ID.get(),
83103
remoteBlockNum);
84104
} catch (Exception e) {
@@ -88,6 +108,15 @@ private void run() {
88108
}
89109
}
90110

111+
public void shutdown() {
112+
flag = false;
113+
// Signal both pools before awaiting either so they drain concurrently
114+
getBlockEs.shutdown();
115+
processBlockEs.shutdown();
116+
ExecutorServiceManager.shutdownAndAwaitTermination(getBlockEs, "solid-get-block");
117+
ExecutorServiceManager.shutdownAndAwaitTermination(processBlockEs, "solid-process-block");
118+
}
119+
91120
private void getBlock() {
92121
long blockNum = ID.incrementAndGet();
93122
while (flag) {
@@ -137,7 +166,7 @@ private void loopProcessBlock(Block block) {
137166
}
138167

139168
private Block getBlockByNum(long blockNum) {
140-
while (true) {
169+
while (flag) {
141170
try {
142171
long time = System.currentTimeMillis();
143172
Block block = databaseGrpcClient.getBlock(blockNum);
@@ -155,10 +184,11 @@ private Block getBlockByNum(long blockNum) {
155184
sleep(exceptionSleepTime);
156185
}
157186
}
187+
return null;
158188
}
159189

160190
private long getLastSolidityBlockNum() {
161-
while (true) {
191+
while (flag) {
162192
try {
163193
long time = System.currentTimeMillis();
164194
long blockNum = databaseGrpcClient.getDynamicProperties().getLastSolidityBlockNum();
@@ -171,6 +201,7 @@ private long getLastSolidityBlockNum() {
171201
sleep(exceptionSleepTime);
172202
}
173203
}
204+
return 0;
174205
}
175206

176207
public void sleep(long time) {
@@ -193,4 +224,4 @@ private void resolveCompatibilityIssueIfUsingFullNodeDatabase() {
193224
chainBaseManager.getDynamicPropertiesStore().saveLatestSolidifiedBlockNum(headBlockNum);
194225
}
195226
}
196-
}
227+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package org.tron.common;
2+
3+
import io.grpc.ManagedChannel;
4+
import java.util.concurrent.TimeUnit;
5+
import org.tron.common.application.ApplicationFactory;
6+
import org.tron.common.application.TronApplicationContext;
7+
import org.tron.core.config.DefaultConfig;
8+
9+
/**
10+
* Shared class-level fixture for tests that manually manage a TronApplicationContext.
11+
*/
12+
public class ClassLevelAppContextFixture {
13+
14+
private TronApplicationContext context;
15+
16+
public TronApplicationContext createContext() {
17+
context = new TronApplicationContext(DefaultConfig.class);
18+
return context;
19+
}
20+
21+
public TronApplicationContext createAndStart() {
22+
createContext();
23+
startApp();
24+
return context;
25+
}
26+
27+
public void startApp() {
28+
ApplicationFactory.create(context).startup();
29+
}
30+
31+
public TronApplicationContext getContext() {
32+
return context;
33+
}
34+
35+
public void close() {
36+
if (context != null) {
37+
context.close();
38+
context = null;
39+
}
40+
}
41+
42+
public static void shutdownChannel(ManagedChannel channel) {
43+
if (channel == null) {
44+
return;
45+
}
46+
try {
47+
channel.shutdown();
48+
if (!channel.awaitTermination(5, TimeUnit.SECONDS)) {
49+
channel.shutdownNow();
50+
}
51+
} catch (InterruptedException e) {
52+
channel.shutdownNow();
53+
Thread.currentThread().interrupt();
54+
}
55+
}
56+
57+
public static void shutdownChannels(ManagedChannel... channels) {
58+
for (ManagedChannel channel : channels) {
59+
shutdownChannel(channel);
60+
}
61+
}
62+
}

framework/src/test/java/org/tron/common/backup/BackupServerTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public void tearDown() {
4646
@Test(timeout = 60_000)
4747
public void test() throws InterruptedException {
4848
backupServer.initServer();
49-
// wait for the server to start
49+
// wait for the server to start so channel is assigned before close() is called
5050
Thread.sleep(1000);
5151
}
5252
}

framework/src/test/java/org/tron/common/logsfilter/EventParserJsonTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,6 @@ public synchronized void testEventParser() {
6565

6666
for (int i = 0; i < entryArr.size(); i++) {
6767
JSONObject e = entryArr.getJSONObject(i);
68-
System.out.println(e.getString("name"));
6968
if (e.getString("name") != null) {
7069
if (e.getString("name").equalsIgnoreCase("eventBytesL")) {
7170
entry = e;

framework/src/test/java/org/tron/common/logsfilter/EventParserTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ public synchronized void testEventParser() {
6868

6969
ABI.Entry entry = null;
7070
for (ABI.Entry e : abi.getEntrysList()) {
71-
System.out.println(e.getName());
7271
if (e.getName().equalsIgnoreCase("eventBytesL")) {
7372
entry = e;
7473
break;

framework/src/test/java/org/tron/common/logsfilter/NativeMessageQueueTest.java

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package org.tron.common.logsfilter;
22

3+
import java.util.concurrent.ExecutorService;
4+
import org.junit.After;
35
import org.junit.Assert;
46
import org.junit.Test;
7+
import org.tron.common.es.ExecutorServiceManager;
58
import org.tron.common.logsfilter.nativequeue.NativeMessageQueue;
69
import org.zeromq.SocketType;
710
import org.zeromq.ZContext;
@@ -13,6 +16,15 @@ public class NativeMessageQueueTest {
1316
public String dataToSend = "################";
1417
public String topic = "testTopic";
1518

19+
private ExecutorService subscriberExecutor;
20+
private final String zmqSubscriber = "zmq-subscriber";
21+
22+
@After
23+
public void tearDown() {
24+
ExecutorServiceManager.shutdownAndAwaitTermination(subscriberExecutor, zmqSubscriber);
25+
subscriberExecutor = null;
26+
}
27+
1628
@Test
1729
public void invalidBindPort() {
1830
boolean bRet = NativeMessageQueue.getInstance().start(-1111, 0);
@@ -39,22 +51,23 @@ public void publishTrigger() {
3951
try {
4052
Thread.sleep(1000);
4153
} catch (InterruptedException e) {
42-
e.printStackTrace();
54+
Thread.currentThread().interrupt();
4355
}
4456

4557
NativeMessageQueue.getInstance().publishTrigger(dataToSend, topic);
4658

4759
try {
4860
Thread.sleep(1000);
4961
} catch (InterruptedException e) {
50-
e.printStackTrace();
62+
Thread.currentThread().interrupt();
5163
}
5264

5365
NativeMessageQueue.getInstance().stop();
5466
}
5567

5668
public void startSubscribeThread() {
57-
Thread thread = new Thread(() -> {
69+
subscriberExecutor = ExecutorServiceManager.newSingleThreadExecutor(zmqSubscriber);
70+
subscriberExecutor.execute(() -> {
5871
try (ZContext context = new ZContext()) {
5972
ZMQ.Socket subscriber = context.createSocket(SocketType.SUB);
6073

@@ -70,6 +83,5 @@ public void startSubscribeThread() {
7083
// ZMQ.Socket will be automatically closed when ZContext is closed
7184
}
7285
});
73-
thread.start();
7486
}
7587
}

framework/src/test/java/org/tron/common/logsfilter/capsule/BlockFilterCapsuleTest.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ public void setUp() {
2222
public void testSetAndGetBlockHash() {
2323
blockFilterCapsule
2424
.setBlockHash("e58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f");
25-
System.out.println(blockFilterCapsule);
2625
Assert.assertEquals("e58f33f9baf9305dc6f82b9f1934ea8f0ade2defb951258d50167028c780351f",
2726
blockFilterCapsule.getBlockHash());
2827
}

framework/src/test/java/org/tron/common/runtime/vm/BandWidthRuntimeTest.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.junit.BeforeClass;
2525
import org.junit.Test;
2626
import org.tron.common.BaseTest;
27+
import org.tron.common.parameter.CommonParameter;
2728
import org.tron.common.runtime.RuntimeImpl;
2829
import org.tron.common.runtime.TvmTestUtils;
2930
import org.tron.common.utils.Commons;
@@ -153,8 +154,13 @@ public void testSuccess() {
153154

154155
@Test
155156
public void testSuccessNoBandd() {
157+
boolean originalDebug = CommonParameter.getInstance().isDebug();
156158
try {
157159
byte[] contractAddress = createContract();
160+
// Enable debug mode to bypass CPU time limit check in Program.checkCPUTimeLimit().
161+
// Without this, the heavy contract execution (setCoin) may exceed the time threshold
162+
// on slow machines and cause the test to fail non-deterministically.
163+
CommonParameter.getInstance().setDebug(true);
158164
TriggerSmartContract triggerContract = TvmTestUtils.createTriggerContract(contractAddress,
159165
"setCoin(uint256)", "50", false,
160166
0, Commons.decodeFromBase58Check(TriggerOwnerTwoAddress));
@@ -185,6 +191,8 @@ public void testSuccessNoBandd() {
185191
balance);
186192
} catch (TronException e) {
187193
Assert.assertNotNull(e);
194+
} finally {
195+
CommonParameter.getInstance().setDebug(originalDebug);
188196
}
189197
}
190198

@@ -254,4 +262,4 @@ public void testMaxContractResultSize() {
254262
}
255263
Assert.assertEquals(2, maxSize);
256264
}
257-
}
265+
}

framework/src/test/java/org/tron/common/runtime/vm/FreezeTest.java

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -289,17 +289,9 @@ public void testWithCallerEnergyChangedInTx() throws Exception {
289289

290290
TVMTestResult result = freezeForOther(userA, contractAddr, userA, frozenBalance, 1);
291291

292-
System.out.println(result.getReceipt().getEnergyUsageTotal());
293-
System.out.println(accountStore.get(userA));
294-
System.out.println(accountStore.get(owner));
295-
296292
clearDelegatedExpireTime(contractAddr, userA);
297293

298294
result = unfreezeForOther(userA, contractAddr, userA, 1);
299-
300-
System.out.println(result.getReceipt().getEnergyUsageTotal());
301-
System.out.println(accountStore.get(userA));
302-
System.out.println(accountStore.get(owner));
303295
}
304296

305297
@Test

framework/src/test/java/org/tron/common/runtime/vm/VoteWitnessCost3Test.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,6 @@ public void testZeroLengthOneArray() {
124124
// witness array zero, amount array non-zero
125125
Program program = mockProgram(0, 0, 64, 1, 0);
126126
long cost = EnergyCost.getVoteWitnessCost3(program);
127-
// witnessArraySize = 0 * 32 + 32 = 32, witnessMemNeeded = 0 + 32 = 32
128-
// amountArraySize = 1 * 32 + 32 = 64, amountMemNeeded = 64 + 64 = 128
129127
// memWords = 128 / 32 = 4
130128
// memEnergy = 3 * 4 + 4 * 4 / 512 = 12
131129
assertEquals(30012, cost);

0 commit comments

Comments
 (0)