From 94f1cd41d02cf5ea545c3e73d51ddaee79d609a3 Mon Sep 17 00:00:00 2001 From: halibobo1205 Date: Wed, 4 Dec 2024 20:42:23 +0800 Subject: [PATCH] feat(all): add exit manager --- build.gradle | 4 + .../leveldb/LevelDbDataSourceImpl.java | 17 ++-- .../rocksdb/RocksDbDataSourceImpl.java | 11 ++- .../tron/core/db2/core/SnapshotManager.java | 33 +++----- .../tron/core/service/RewardViCalService.java | 6 +- .../org/tron/common/exit/ExitManager.java | 79 +++++++++++++++++++ .../java/org/tron/common/exit/TronExit.java | 13 +++ .../core/exception/ConfigExitException.java | 19 +++++ .../core/exception/DatabaseExitException.java | 19 +++++ .../core/exception/EventExitException.java | 19 +++++ .../core/exception/OtherExitException.java | 19 +++++ .../core/exception/TronExitException.java | 23 ++++++ framework/build.gradle | 1 - .../java/org/tron/core/config/args/Args.java | 10 +-- .../main/java/org/tron/core/db/Manager.java | 43 +++++----- .../org/tron/core/net/TronNetDelegate.java | 41 ++-------- .../java/org/tron/program/SolidityNode.java | 8 +- .../leveldb/LevelDbDataSourceImplTest.java | 7 +- .../leveldb/RocksDbDataSourceImplTest.java | 7 +- .../java/org/tron/core/db/ManagerTest.java | 29 +------ .../tron/core/services/ComputeRewardTest.java | 7 +- .../services/stop/ConditionallyStopTest.java | 2 - 22 files changed, 276 insertions(+), 141 deletions(-) create mode 100644 common/src/main/java/org/tron/common/exit/ExitManager.java create mode 100644 common/src/main/java/org/tron/common/exit/TronExit.java create mode 100644 common/src/main/java/org/tron/core/exception/ConfigExitException.java create mode 100644 common/src/main/java/org/tron/core/exception/DatabaseExitException.java create mode 100644 common/src/main/java/org/tron/core/exception/EventExitException.java create mode 100644 common/src/main/java/org/tron/core/exception/OtherExitException.java create mode 100644 common/src/main/java/org/tron/core/exception/TronExitException.java diff --git a/build.gradle b/build.gradle index b1aba1c0aa9..53fe6da5f6c 100644 --- a/build.gradle +++ b/build.gradle @@ -67,6 +67,10 @@ subprojects { reproducibleFileOrder = true duplicatesStrategy = DuplicatesStrategy.INCLUDE // allow duplicates } + tasks.withType(Test).configureEach { + // https://docs.gradle.org/current/dsl/org.gradle.api.tasks.testing.Test.html#org.gradle.api.tasks.testing.Test:environment + environment 'CI', 'true' + } } task copyToParent(type: Copy) { diff --git a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java index 43a24ff4416..42be04bc5a6 100644 --- a/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/leveldb/LevelDbDataSourceImpl.java @@ -18,7 +18,7 @@ import static org.fusesource.leveldbjni.JniDBFactory.factory; import com.google.common.collect.Sets; -import java.io.File; +import com.google.common.primitives.Bytes; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -30,18 +30,14 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.stream.Collectors; import java.util.stream.Stream; import java.util.stream.StreamSupport; - -import com.google.common.primitives.Bytes; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.iq80.leveldb.CompressionType; import org.iq80.leveldb.DB; import org.iq80.leveldb.DBIterator; import org.iq80.leveldb.Logger; @@ -50,6 +46,7 @@ import org.iq80.leveldb.WriteBatch; import org.iq80.leveldb.WriteOptions; import org.slf4j.LoggerFactory; +import org.tron.common.exit.ExitManager; import org.tron.common.parameter.CommonParameter; import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.metric.DbStat; @@ -59,6 +56,7 @@ import org.tron.core.db.common.iterator.StoreIterator; import org.tron.core.db2.common.Instance; import org.tron.core.db2.common.WrappedByteArray; +import org.tron.core.exception.DatabaseExitException; @Slf4j(topic = "DB") @NoArgsConstructor @@ -151,13 +149,14 @@ private void openDatabase(Options dbOptions) throws IOException { dbOptions.cacheSize() / 1024 / 1024, dbOptions.maxOpenFiles()); } } catch (IOException e) { + String msg; if (e.getMessage().contains("Corruption:")) { - logger.error("Database {} corrupted, please delete database directory({}) and restart.", - dataBaseName, parentPath, e); + msg = String.format("Database %s corrupted, please delete database directory(%s) " + + "and restart.", dataBaseName, parentPath); } else { - logger.error("Open Database {} failed", dataBaseName, e); + msg = String.format("Open Database %s failed", dataBaseName); } - System.exit(1); + ExitManager.exit(msg, new DatabaseExitException(e)); } } diff --git a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java index 6c5d8018487..156abb4d95c 100644 --- a/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java +++ b/chainbase/src/main/java/org/tron/common/storage/rocksdb/RocksDbDataSourceImpl.java @@ -36,6 +36,7 @@ import org.rocksdb.WriteBatch; import org.rocksdb.WriteOptions; import org.slf4j.LoggerFactory; +import org.tron.common.exit.ExitManager; import org.tron.common.setting.RocksDbSettings; import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.storage.metric.DbStat; @@ -45,6 +46,7 @@ import org.tron.core.db.common.iterator.RockStoreIterator; import org.tron.core.db2.common.Instance; import org.tron.core.db2.common.WrappedByteArray; +import org.tron.core.exception.DatabaseExitException; @Slf4j(topic = "DB") @@ -266,13 +268,14 @@ protected void log(InfoLogLevel infoLogLevel, String logMsg) { try { database = RocksDB.open(options, dbPath.toString()); } catch (RocksDBException e) { + String msg; if (Objects.equals(e.getStatus().getCode(), Status.Code.Corruption)) { - logger.error("Database {} corrupted, please delete database directory({}) " + - "and restart.", dataBaseName, parentPath, e); + msg = String.format("Database %s corrupted, please delete database directory(%s) " + + "and restart.", dataBaseName, parentPath); } else { - logger.error("Open Database {} failed", dataBaseName, e); + msg = String.format("Open Database %s failed", dataBaseName); } - System.exit(1); + ExitManager.exit(msg, new DatabaseExitException(e)); } alive = true; diff --git a/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java b/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java index f0f169ae340..b122f467bbd 100644 --- a/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java +++ b/chainbase/src/main/java/org/tron/core/db2/core/SnapshotManager.java @@ -20,7 +20,6 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.LockSupport; import java.util.stream.Collectors; import javax.annotation.PostConstruct; import lombok.Getter; @@ -29,6 +28,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.tron.common.error.TronDBException; import org.tron.common.es.ExecutorServiceManager; +import org.tron.common.exit.ExitManager; import org.tron.common.parameter.CommonParameter; import org.tron.common.storage.WriteOptionsWrapper; import org.tron.common.utils.FileUtil; @@ -41,6 +41,8 @@ import org.tron.core.db2.common.Key; import org.tron.core.db2.common.Value; import org.tron.core.db2.common.WrappedByteArray; +import org.tron.core.exception.ConfigExitException; +import org.tron.core.exception.DatabaseExitException; import org.tron.core.exception.RevokingStoreIllegalStateException; import org.tron.core.store.CheckPointV2Store; import org.tron.core.store.CheckTmpStore; @@ -68,7 +70,6 @@ public class SnapshotManager implements RevokingDatabase { private volatile int flushCount = 0; - private Thread exitThread; private volatile boolean hitDown; private Map flushServices = new HashMap<>(); @@ -105,15 +106,6 @@ public void init() { } }, 10000, 3600, TimeUnit.MILLISECONDS); } - exitThread = new Thread(() -> { - LockSupport.park(); - // to Guarantee Some other thread invokes unpark with the current thread as the target - if (hitDown) { - System.exit(1); - } - }); - exitThread.setName("exit-thread"); - exitThread.start(); } public static String simpleDecode(byte[] bytes) { @@ -281,13 +273,6 @@ public void shutdown() { ExecutorServiceManager.shutdownAndAwaitTermination(pruneCheckpointThread, pruneName); flushServices.forEach((key, value) -> ExecutorServiceManager.shutdownAndAwaitTermination(value, "flush-service-" + key)); - try { - exitThread.interrupt(); - // help GC - exitThread = null; - } catch (Exception e) { - logger.warn("exitThread interrupt error", e); - } } public void updateSolidity(int hops) { @@ -365,9 +350,9 @@ public void flush() { System.currentTimeMillis() - checkPointEnd ); } catch (TronDBException e) { - logger.error(" Find fatal error, program will be exited soon.", e); + String msg = " Find fatal error, program will be exited soon."; hitDown = true; - LockSupport.unpark(exitThread); + ExitManager.exit(msg, new DatabaseExitException(e)); } } } @@ -490,10 +475,10 @@ public void check() { if (!isV2Open()) { List cpList = getCheckpointList(); if (cpList != null && cpList.size() != 0) { - logger.error("checkpoint check failed, the checkpoint version of database not match your " + - "config file, please set storage.checkpoint.version = 2 in your config file " + - "and restart the node."); - System.exit(-1); + String msg = "checkpoint check failed, the checkpoint version of database not match your " + + "config file, please set storage.checkpoint.version = 2 in your config file " + + "and restart the node."; + ExitManager.exit(new ConfigExitException(msg)); } checkV1(); } else { diff --git a/chainbase/src/main/java/org/tron/core/service/RewardViCalService.java b/chainbase/src/main/java/org/tron/core/service/RewardViCalService.java index e27990f0403..05ac81543f9 100644 --- a/chainbase/src/main/java/org/tron/core/service/RewardViCalService.java +++ b/chainbase/src/main/java/org/tron/core/service/RewardViCalService.java @@ -23,6 +23,7 @@ import org.springframework.stereotype.Component; import org.tron.common.error.TronDBException; import org.tron.common.es.ExecutorServiceManager; +import org.tron.common.exit.ExitManager; import org.tron.common.parameter.CommonParameter; import org.tron.common.utils.ByteArray; import org.tron.common.utils.MerkleRoot; @@ -30,6 +31,7 @@ import org.tron.common.utils.Sha256Hash; import org.tron.core.db.common.iterator.DBIterator; import org.tron.core.db2.common.DB; +import org.tron.core.exception.DatabaseExitException; import org.tron.core.store.DelegationStore; import org.tron.core.store.DynamicPropertiesStore; import org.tron.core.store.RewardViStore; @@ -120,8 +122,8 @@ private void maybeRun() { } } } catch (Exception e) { - logger.error(" Find fatal error, program will be exited soon.", e); - System.exit(1); + String msg = " Find fatal error, program will be exited soon."; + ExitManager.exit(msg, new DatabaseExitException(e)); } } diff --git a/common/src/main/java/org/tron/common/exit/ExitManager.java b/common/src/main/java/org/tron/common/exit/ExitManager.java new file mode 100644 index 00000000000..810e757094a --- /dev/null +++ b/common/src/main/java/org/tron/common/exit/ExitManager.java @@ -0,0 +1,79 @@ +package org.tron.common.exit; + +import java.util.Arrays; +import java.util.Objects; +import java.util.concurrent.ThreadFactory; +import lombok.extern.slf4j.Slf4j; +import org.tron.core.exception.TronExitException; + +@Slf4j(topic = "Exit") +public class ExitManager { + + private static final String[] CI_ENVIRONMENT_VARIABLES = { + "CI", + "JENKINS_URL", + "TRAVIS", + "CIRCLECI", + "GITHUB_ACTIONS", + "GITLAB_CI" + }; + + private static final int EXIT_CODE_NORMAL = 0; + + private static final ThreadFactory exitThreadFactory = r -> { + Thread thread = new Thread(r, "System-Exit-Thread"); + thread.setDaemon(true); + return thread; + }; + + private ExitManager() { + } + + public static void exit() { + exit((String) null); + } + + public static void exit(String msg) { + exit(msg, null); + } + + public static void exit(TronExitException cause) { + exit(cause.getMessage(), cause); + } + + public static void exit(String msg, TronExitException cause) { + TronExit exit = new TronExit(msg, cause); + if (isRunningInCI()) { + if (Objects.nonNull(cause)) { + throw cause; + } else if (Objects.nonNull(msg)) { + logger.info("{}", msg); + } + } else { + logAndExit(exit); + } + } + + private static boolean isRunningInCI() { + return Arrays.stream(CI_ENVIRONMENT_VARIABLES).anyMatch(System.getenv()::containsKey); + } + + private static void logAndExit(TronExit exit) { + String msg = exit.getMsg(); + TronExitException cause = exit.getException(); + final int code = Objects.isNull(cause) ? EXIT_CODE_NORMAL : cause.getExitCode(); + if (code == EXIT_CODE_NORMAL) { + if (Objects.nonNull(msg)) { + logger.info("Exiting, {}.", msg); + } + } else { + if (Objects.isNull(msg)) { + logger.error("Exiting with code: {}.", code, cause); + } else { + logger.error("Exiting with code: {}, {}.", code, msg, cause); + } + } + Thread exitThread = exitThreadFactory.newThread(() -> System.exit(code)); + exitThread.start(); + } +} \ No newline at end of file diff --git a/common/src/main/java/org/tron/common/exit/TronExit.java b/common/src/main/java/org/tron/common/exit/TronExit.java new file mode 100644 index 00000000000..be94207e45c --- /dev/null +++ b/common/src/main/java/org/tron/common/exit/TronExit.java @@ -0,0 +1,13 @@ +package org.tron.common.exit; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.tron.core.exception.TronExitException; + +@Getter +@AllArgsConstructor +public class TronExit { + + private String msg; + private TronExitException exception; +} diff --git a/common/src/main/java/org/tron/core/exception/ConfigExitException.java b/common/src/main/java/org/tron/core/exception/ConfigExitException.java new file mode 100644 index 00000000000..f6e033772a4 --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/ConfigExitException.java @@ -0,0 +1,19 @@ +package org.tron.core.exception; + +public class ConfigExitException extends TronExitException { + + public ConfigExitException(String message) { + super(message); + setExitCode(1); + } + + public ConfigExitException(String message, Throwable cause) { + super(message, cause); + setExitCode(1); + } + + public ConfigExitException(Throwable cause) { + super(cause); + setExitCode(1); + } +} diff --git a/common/src/main/java/org/tron/core/exception/DatabaseExitException.java b/common/src/main/java/org/tron/core/exception/DatabaseExitException.java new file mode 100644 index 00000000000..0644ce2ef7b --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/DatabaseExitException.java @@ -0,0 +1,19 @@ +package org.tron.core.exception; + +public class DatabaseExitException extends TronExitException { + + public DatabaseExitException(String message) { + super(message); + setExitCode(2); + } + + public DatabaseExitException(String message, Throwable cause) { + super(message, cause); + setExitCode(2); + } + + public DatabaseExitException(Throwable cause) { + super(cause); + setExitCode(2); + } +} diff --git a/common/src/main/java/org/tron/core/exception/EventExitException.java b/common/src/main/java/org/tron/core/exception/EventExitException.java new file mode 100644 index 00000000000..665dc3604e8 --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/EventExitException.java @@ -0,0 +1,19 @@ +package org.tron.core.exception; + +public class EventExitException extends TronExitException { + + public EventExitException(String message) { + super(message); + setExitCode(3); + } + + public EventExitException(String message, Throwable cause) { + super(message, cause); + setExitCode(3); + } + + public EventExitException(Throwable cause) { + super(cause); + setExitCode(3); + } +} diff --git a/common/src/main/java/org/tron/core/exception/OtherExitException.java b/common/src/main/java/org/tron/core/exception/OtherExitException.java new file mode 100644 index 00000000000..89584f15ff6 --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/OtherExitException.java @@ -0,0 +1,19 @@ +package org.tron.core.exception; + +public class OtherExitException extends TronExitException { + + public OtherExitException(String message) { + super(message); + setExitCode(99); + } + + public OtherExitException(String message, Throwable cause) { + super(message, cause); + setExitCode(99); + } + + public OtherExitException(Throwable cause) { + super(cause); + setExitCode(99); + } +} diff --git a/common/src/main/java/org/tron/core/exception/TronExitException.java b/common/src/main/java/org/tron/core/exception/TronExitException.java new file mode 100644 index 00000000000..397309383bc --- /dev/null +++ b/common/src/main/java/org/tron/core/exception/TronExitException.java @@ -0,0 +1,23 @@ +package org.tron.core.exception; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class TronExitException extends RuntimeException { + + private int exitCode; + + public TronExitException(String message) { + super(message); + } + + public TronExitException(String message, Throwable cause) { + super(message, cause); + } + + public TronExitException(Throwable cause) { + super(cause); + } +} diff --git a/framework/build.gradle b/framework/build.gradle index ec113c93cb1..dda5b23921c 100644 --- a/framework/build.gradle +++ b/framework/build.gradle @@ -42,7 +42,6 @@ dependencies { implementation fileTree(dir: 'libs', include: '*.jar') // end local libraries testImplementation group: 'org.hamcrest', name: 'hamcrest-junit', version: '1.0.0.1' - testImplementation group: 'com.github.stefanbirkner', name: 'system-rules', version: '1.16.0' implementation group: 'com.google.inject', name: 'guice', version: '4.1.0' implementation group: 'io.dropwizard.metrics', name: 'metrics-core', version: '3.1.2' diff --git a/framework/src/main/java/org/tron/core/config/args/Args.java b/framework/src/main/java/org/tron/core/config/args/Args.java index 8853971a5f8..c1e5a848bd0 100644 --- a/framework/src/main/java/org/tron/core/config/args/Args.java +++ b/framework/src/main/java/org/tron/core/config/args/Args.java @@ -1,7 +1,6 @@ package org.tron.core.config.args; import static java.lang.Math.max; -import static java.lang.System.exit; import static org.tron.core.Constant.ADD_PRE_FIX_BYTE_MAINNET; import static org.tron.core.Constant.DYNAMIC_ENERGY_INCREASE_FACTOR_RANGE; import static org.tron.core.Constant.DYNAMIC_ENERGY_MAX_FACTOR_RANGE; @@ -49,6 +48,7 @@ import org.tron.common.args.Witness; import org.tron.common.config.DbBackupConfig; import org.tron.common.crypto.SignInterface; +import org.tron.common.exit.ExitManager; import org.tron.common.logsfilter.EventPluginConfig; import org.tron.common.logsfilter.FilterQuery; import org.tron.common.logsfilter.TriggerConfig; @@ -66,6 +66,7 @@ import org.tron.core.config.Parameter.NetConstants; import org.tron.core.config.Parameter.NodeConstant; import org.tron.core.exception.CipherException; +import org.tron.core.exception.ConfigExitException; import org.tron.core.store.AccountStore; import org.tron.keystore.Credentials; import org.tron.keystore.WalletUtils; @@ -359,7 +360,7 @@ public static void setParam(final String[] args, final String confFileName) { JCommander.newBuilder().addObject(PARAMETER).build().parse(args); if (PARAMETER.version) { printVersion(); - exit(0); + ExitManager.exit(); } Config config = Configuration.getByFileName(PARAMETER.shellConfFileName, confFileName); @@ -420,9 +421,8 @@ public static void setParam(final String[] args, final String confFileName) { String prikey = ByteArray.toHexString(sign.getPrivateKey()); privateKeys.add(prikey); } catch (IOException | CipherException e) { - logger.error(e.getMessage()); - logger.error("Witness node start failed!"); - exit(-1); + String msg = "Witness node start failed!"; + ExitManager.exit(msg, new ConfigExitException(e)); } } } diff --git a/framework/src/main/java/org/tron/core/db/Manager.java b/framework/src/main/java/org/tron/core/db/Manager.java index 908e248bdee..65555c3e83e 100644 --- a/framework/src/main/java/org/tron/core/db/Manager.java +++ b/framework/src/main/java/org/tron/core/db/Manager.java @@ -50,6 +50,7 @@ import org.tron.common.args.GenesisBlock; import org.tron.common.bloom.Bloom; import org.tron.common.es.ExecutorServiceManager; +import org.tron.common.exit.ExitManager; import org.tron.common.logsfilter.EventPluginLoader; import org.tron.common.logsfilter.FilterQuery; import org.tron.common.logsfilter.capsule.BlockFilterCapsule; @@ -111,11 +112,14 @@ import org.tron.core.exception.BadItemException; import org.tron.core.exception.BadNumberBlockException; import org.tron.core.exception.BalanceInsufficientException; +import org.tron.core.exception.ConfigExitException; import org.tron.core.exception.ContractExeException; import org.tron.core.exception.ContractSizeNotEqualToOneException; import org.tron.core.exception.ContractValidateException; +import org.tron.core.exception.DatabaseExitException; import org.tron.core.exception.DupTransactionException; import org.tron.core.exception.EventBloomException; +import org.tron.core.exception.EventExitException; import org.tron.core.exception.ItemNotFoundException; import org.tron.core.exception.NonCommonBlockException; import org.tron.core.exception.ReceiptCheckErrException; @@ -491,20 +495,20 @@ public void init() { this.khaosDb.start(chainBaseManager.getBlockById( getDynamicPropertiesStore().getLatestBlockHeaderHash())); } catch (ItemNotFoundException e) { - logger.error( - "Can not find Dynamic highest block from DB! \nnumber={} \nhash={}", + String msg = String.format( + "Can not find Dynamic highest block from DB! \nnumber=%d \nhash=%s", getDynamicPropertiesStore().getLatestBlockHeaderNumber(), getDynamicPropertiesStore().getLatestBlockHeaderHash()); - logger.error( - "Please delete database directory({}) and restart", + String tip = String.format( + "Please delete database directory(%s) and restart", Args.getInstance().getOutputDirectory()); - System.exit(1); + ExitManager.exit(tip, new DatabaseExitException(msg, e)); } catch (BadItemException e) { - logger.error("DB data broken {}.", e.getMessage()); - logger.error( - "Please delete database directory({}) and restart.", + String msg = "DB data broken"; + String tip = String.format( + "Please delete database directory(%s) and restart", Args.getInstance().getOutputDirectory()); - System.exit(1); + ExitManager.exit(tip, new DatabaseExitException(msg, e)); } getChainBaseManager().getForkController().init(this.chainBaseManager); @@ -568,8 +572,8 @@ public void init() { try { initAutoStop(); } catch (IllegalArgumentException e) { - logger.error("Auto-stop params error: {}", e.getMessage()); - System.exit(1); + String msg = "Auto-stop params error"; + ExitManager.exit(msg, new ConfigExitException(e)); } maxFlushCount = CommonParameter.getInstance().getStorage().getMaxFlushCount(); @@ -586,10 +590,9 @@ public void initGenesis() { Args.getInstance().setChainId(genesisBlock.getBlockId().toString()); } else { if (chainBaseManager.hasBlocks()) { - logger.error( - "Genesis block modify, please delete database directory({}) and restart.", - Args.getInstance().getOutputDirectory()); - System.exit(1); + String msg = String.format("Genesis block modify, please delete database directory(%s) and " + + "restart.", Args.getInstance().getOutputDirectory()); + ExitManager.exit(new DatabaseExitException(msg)); } else { logger.info("Create genesis block."); Args.getInstance().setChainId(genesisBlock.getBlockId().toString()); @@ -741,9 +744,9 @@ private void initAutoStop() { } if (exitHeight == headNum && (!Args.getInstance().isP2pDisable())) { - logger.info("Auto-stop hit: shutDownBlockHeight: {}, currentHeaderNum: {}, exit now", + String msg = String.format("Auto-stop hit: shutDownBlockHeight: %d, currentHeaderNum: %d", exitHeight, headNum); - System.exit(0); + ExitManager.exit(msg); } if (exitCount > 0) { @@ -1368,9 +1371,9 @@ void blockTrigger(final BlockCapsule block, long oldSolid, long newSolid) { // if event subscribe is enabled, post solidity trigger to queue postSolidityTrigger(oldSolid, newSolid); } catch (Exception e) { - logger.error("Block trigger failed. head: {}, oldSolid: {}, newSolid: {}", - block.getNum(), oldSolid, newSolid, e); - System.exit(1); + String msg = String.format("Block trigger failed. head: %d, oldSolid: %d, newSolid: %d", + block.getNum(), oldSolid, newSolid); + ExitManager.exit(msg, new EventExitException(e)); } } diff --git a/framework/src/main/java/org/tron/core/net/TronNetDelegate.java b/framework/src/main/java/org/tron/core/net/TronNetDelegate.java index a6f9812a2d7..9e5017400d4 100644 --- a/framework/src/main/java/org/tron/core/net/TronNetDelegate.java +++ b/framework/src/main/java/org/tron/core/net/TronNetDelegate.java @@ -9,16 +9,13 @@ import java.util.Collection; import java.util.List; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.LockSupport; -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; import lombok.Getter; -import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.bouncycastle.util.encoders.Hex; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.tron.common.backup.socket.BackupServer; +import org.tron.common.exit.ExitManager; import org.tron.common.overlay.message.Message; import org.tron.common.prometheus.MetricKeys; import org.tron.common.prometheus.MetricLabels; @@ -91,14 +88,9 @@ public class TronNetDelegate { private long timeout = 1000; - @Getter // for test + @Getter private volatile boolean hitDown = false; - private Thread hitThread; - - @Setter - private volatile boolean exit = true; - private int maxUnsolidifiedBlocks = Args.getInstance().getMaxUnsolidifiedBlocks(); private boolean unsolidifiedBlockCheck @@ -108,30 +100,6 @@ public class TronNetDelegate { .maximumSize(blockIdCacheSize).expireAfterWrite(1, TimeUnit.HOURS) .recordStats().build(); - @PostConstruct - public void init() { - hitThread = new Thread(() -> { - LockSupport.park(); - // to Guarantee Some other thread invokes unpark with the current thread as the target - if (hitDown && exit) { - System.exit(0); - } - }); - hitThread.setName("hit-thread"); - hitThread.start(); - } - - @PreDestroy - public void close() { - try { - hitThread.interrupt(); - // help GC - hitThread = null; - } catch (Exception e) { - logger.warn("hitThread interrupt error", e); - } - } - public Collection getActivePeer() { return TronNetService.getPeers(); } @@ -234,12 +202,13 @@ public void processBlock(BlockCapsule block, boolean isSync) throws P2pException && dbManager.getLatestSolidityNumShutDown() == dbManager.getDynamicPropertiesStore() .getLatestBlockHeaderNumberFromDB()) { - logger.info("Begin shutdown, currentBlockNum:{}, DbBlockNum:{}, solidifiedBlockNum:{}", + String msg = String.format("Begin shutdown, currentBlockNum:%d, DbBlockNum:%d, " + + "solidifiedBlockNum:%d", dbManager.getDynamicPropertiesStore().getLatestBlockHeaderNumber(), dbManager.getDynamicPropertiesStore().getLatestBlockHeaderNumberFromDB(), dbManager.getDynamicPropertiesStore().getLatestSolidifiedBlockNum()); hitDown = true; - LockSupport.unpark(hitThread); + ExitManager.exit(msg); return; } if (hitDown) { diff --git a/framework/src/main/java/org/tron/program/SolidityNode.java b/framework/src/main/java/org/tron/program/SolidityNode.java index 4cf71177803..7ff43a1b85a 100644 --- a/framework/src/main/java/org/tron/program/SolidityNode.java +++ b/framework/src/main/java/org/tron/program/SolidityNode.java @@ -6,13 +6,12 @@ import java.util.concurrent.atomic.AtomicLong; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.BooleanUtils; -import org.springframework.context.ApplicationContext; import org.springframework.util.ObjectUtils; -import org.springframework.util.StringUtils; import org.tron.common.application.Application; import org.tron.common.application.ApplicationFactory; import org.tron.common.application.TronApplicationContext; import org.tron.common.client.DatabaseGrpcClient; +import org.tron.common.exit.ExitManager; import org.tron.common.parameter.CommonParameter; import org.tron.common.prometheus.Metrics; import org.tron.core.ChainBaseManager; @@ -21,6 +20,7 @@ import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; import org.tron.core.db.Manager; +import org.tron.core.exception.OtherExitException; import org.tron.core.services.RpcApiService; import org.tron.core.services.http.solidity.SolidityNodeHttpApiService; import org.tron.protos.Protocol.Block; @@ -103,9 +103,9 @@ private void start() { logger.info("Success to start solid node, ID: {}, remoteBlockNum: {}.", ID.get(), remoteBlockNum); } catch (Exception e) { - logger.error("Failed to start solid node, address: {}.", + String msg = String.format("Failed to start solid node, address: %s.", CommonParameter.getInstance().getTrustNodeAddr()); - System.exit(0); + ExitManager.exit(msg, new OtherExitException(e)); } } diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java index 50a55d24a0f..47b9ca471bb 100644 --- a/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java +++ b/framework/src/test/java/org/tron/common/storage/leveldb/LevelDbDataSourceImplTest.java @@ -43,7 +43,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.ExpectedSystemExit; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.tron.common.utils.ByteArray; import org.tron.common.utils.FileUtil; @@ -51,6 +51,7 @@ import org.tron.core.Constant; import org.tron.core.config.args.Args; import org.tron.core.db2.common.WrappedByteArray; +import org.tron.core.exception.DatabaseExitException; @Slf4j public class LevelDbDataSourceImplTest { @@ -74,7 +75,7 @@ public class LevelDbDataSourceImplTest { private byte[] key6 = "00000006aa".getBytes(); @Rule - public final ExpectedSystemExit exit = ExpectedSystemExit.none(); + public final ExpectedException exception = ExpectedException.none(); /** * Release resources. @@ -350,7 +351,7 @@ public void prefixQueryTest() { @Test public void initDbTest() { - exit.expectSystemExitWithStatus(1); + exception.expect(DatabaseExitException.class); makeExceptionDb("test_initDb"); LevelDbDataSourceImpl dataSource = new LevelDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_initDb"); diff --git a/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java b/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java index ed37c1e4bcd..b78055c9013 100644 --- a/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java +++ b/framework/src/test/java/org/tron/common/storage/leveldb/RocksDbDataSourceImplTest.java @@ -25,7 +25,7 @@ import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.ExpectedSystemExit; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.tron.common.storage.rocksdb.RocksDbDataSourceImpl; import org.tron.common.utils.ByteArray; @@ -34,6 +34,7 @@ import org.tron.common.utils.PublicMethod; import org.tron.core.config.args.Args; import org.tron.core.db2.common.WrappedByteArray; +import org.tron.core.exception.DatabaseExitException; @Slf4j public class RocksDbDataSourceImplTest { @@ -56,7 +57,7 @@ public class RocksDbDataSourceImplTest { private byte[] key6 = "00000006aa".getBytes(); @Rule - public final ExpectedSystemExit exit = ExpectedSystemExit.none(); + public final ExpectedException exception = ExpectedException.none(); /** * Release resources. @@ -392,7 +393,7 @@ public void prefixQueryTest() { @Test public void initDbTest() { - exit.expectSystemExitWithStatus(1); + exception.expect(DatabaseExitException.class); makeExceptionDb("test_initDb"); RocksDbDataSourceImpl dataSource = new RocksDbDataSourceImpl( Args.getInstance().getOutputDirectory(), "test_initDb"); diff --git a/framework/src/test/java/org/tron/core/db/ManagerTest.java b/framework/src/test/java/org/tron/core/db/ManagerTest.java index 07440435f41..2d501f8384e 100755 --- a/framework/src/test/java/org/tron/core/db/ManagerTest.java +++ b/framework/src/test/java/org/tron/core/db/ManagerTest.java @@ -30,7 +30,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.ExpectedSystemExit; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.tron.common.application.TronApplicationContext; import org.tron.common.crypto.ECKey; @@ -59,28 +59,7 @@ import org.tron.core.db.accountstate.AccountStateEntity; import org.tron.core.db.accountstate.TrieService; import org.tron.core.db.accountstate.storetrie.AccountStateStoreTrie; -import org.tron.core.exception.AccountResourceInsufficientException; -import org.tron.core.exception.BadBlockException; -import org.tron.core.exception.BadItemException; -import org.tron.core.exception.BadNumberBlockException; -import org.tron.core.exception.BalanceInsufficientException; -import org.tron.core.exception.ContractExeException; -import org.tron.core.exception.ContractValidateException; -import org.tron.core.exception.DupTransactionException; -import org.tron.core.exception.EventBloomException; -import org.tron.core.exception.HeaderNotFound; -import org.tron.core.exception.ItemNotFoundException; -import org.tron.core.exception.NonCommonBlockException; -import org.tron.core.exception.ReceiptCheckErrException; -import org.tron.core.exception.TaposException; -import org.tron.core.exception.TooBigTransactionException; -import org.tron.core.exception.TooBigTransactionResultException; -import org.tron.core.exception.TransactionExpirationException; -import org.tron.core.exception.UnLinkedBlockException; -import org.tron.core.exception.VMIllegalException; -import org.tron.core.exception.ValidateScheduleException; -import org.tron.core.exception.ValidateSignatureException; -import org.tron.core.exception.ZksnarkException; +import org.tron.core.exception.*; import org.tron.core.store.CodeStore; import org.tron.core.store.DynamicPropertiesStore; import org.tron.core.store.ExchangeStore; @@ -112,7 +91,7 @@ public class ManagerTest extends BlockGenerate { @Rule public TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule - public final ExpectedSystemExit exit = ExpectedSystemExit.none(); + public final ExpectedException exception = ExpectedException.none(); private static AtomicInteger port = new AtomicInteger(0); private static String accountAddress = Wallet.getAddressPreFixString() + "548794500882809695a8a687866e76d4271a1abc"; @@ -1160,7 +1139,7 @@ public void testTooBigTransaction() { @Test public void blockTrigger() { - exit.expectSystemExitWithStatus(1); + exception.expect(EventExitException.class); Manager manager = spy(new Manager()); doThrow(new RuntimeException("postBlockTrigger mock")).when(manager).postBlockTrigger(any()); manager.blockTrigger(new BlockCapsule(Block.newBuilder().build()), 1, 1); diff --git a/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java b/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java index 0082c8728da..a7604604337 100644 --- a/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java +++ b/framework/src/test/java/org/tron/core/services/ComputeRewardTest.java @@ -17,7 +17,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; -import org.junit.contrib.java.lang.system.ExpectedSystemExit; +import org.junit.rules.ExpectedException; import org.junit.rules.TemporaryFolder; import org.tron.common.application.TronApplicationContext; import org.tron.common.error.TronDBException; @@ -30,6 +30,7 @@ import org.tron.core.capsule.WitnessCapsule; import org.tron.core.config.DefaultConfig; import org.tron.core.config.args.Args; +import org.tron.core.exception.DatabaseExitException; import org.tron.core.service.MortgageService; import org.tron.core.service.RewardViCalService; import org.tron.core.store.AccountStore; @@ -115,7 +116,7 @@ public class ComputeRewardTest { public final TemporaryFolder temporaryFolder = new TemporaryFolder(); @Rule - public final ExpectedSystemExit exit = ExpectedSystemExit.none(); + public final ExpectedException exception = ExpectedException.none(); @After public void destroy() { @@ -263,7 +264,7 @@ private void setUp() { @Test public void query() { - exit.expectSystemExitWithStatus(1); + exception.expect(DatabaseExitException.class); Assert.assertEquals(3189, mortgageService.queryReward(OWNER_ADDRESS)); // mock root is error rewardViStore.put("test".getBytes(), "test".getBytes()); diff --git a/framework/src/test/java/org/tron/core/services/stop/ConditionallyStopTest.java b/framework/src/test/java/org/tron/core/services/stop/ConditionallyStopTest.java index dc3d02e7ced..6dd51048e07 100644 --- a/framework/src/test/java/org/tron/core/services/stop/ConditionallyStopTest.java +++ b/framework/src/test/java/org/tron/core/services/stop/ConditionallyStopTest.java @@ -95,7 +95,6 @@ public void init() throws Exception { consensusService.start(); chainManager = dbManager.getChainBaseManager(); tronNetDelegate = context.getBean(TronNetDelegate.class); - tronNetDelegate.setExit(false); currentHeader = dbManager.getDynamicPropertiesStore() .getLatestBlockHeaderNumberFromDB(); @@ -146,7 +145,6 @@ public void testStop() throws Exception { while (!tronNetDelegate.isHitDown()) { generateBlock(witnessAndAccount); } - Assert.assertTrue(tronNetDelegate.isHitDown()); check(); }