Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optimize application shutdown #4410

Merged
merged 2 commits into from
Aug 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 43 additions & 34 deletions core/src/main/java/bisq/core/app/BisqExecutable.java
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet
protected Injector injector;
protected AppModule module;
protected Config config;
private boolean isShutdown = false;
private boolean isShutdownInProgress;

public BisqExecutable(String fullName, String scriptName, String appName, String version) {
this.fullName = fullName;
Expand Down Expand Up @@ -204,47 +204,56 @@ protected void startAppSetup() {
// This might need to be overwritten in case the application is not using all modules
@Override
public void gracefulShutDown(ResultHandler resultHandler) {
if (isShutdown) // prevent double cleanup
log.info("Start graceful shutDown");
if (isShutdownInProgress) {
return;
}

isShutdownInProgress = true;

if (injector == null) {
log.warn("Shut down called before injector was created");
resultHandler.handleResult();
System.exit(0);
}

isShutdown = true;
try {
if (injector != null) {
injector.getInstance(ArbitratorManager.class).shutDown();
injector.getInstance(TradeManager.class).shutDown();
injector.getInstance(DaoSetup.class).shutDown();
injector.getInstance(OpenOfferManager.class).shutDown(() -> {
log.info("OpenOfferManager shutdown completed");
injector.getInstance(ArbitratorManager.class).shutDown();
injector.getInstance(TradeManager.class).shutDown();
injector.getInstance(DaoSetup.class).shutDown();
injector.getInstance(AvoidStandbyModeService.class).shutDown();
injector.getInstance(OpenOfferManager.class).shutDown(() -> {
log.info("OpenOfferManager shutdown completed");

injector.getInstance(BtcWalletService.class).shutDown();
injector.getInstance(BsqWalletService.class).shutDown();

// We need to shutdown BitcoinJ before the P2PService as it uses Tor.
WalletsSetup walletsSetup = injector.getInstance(WalletsSetup.class);
walletsSetup.shutDownComplete.addListener((ov, o, n) -> {
log.info("WalletsSetup shutdown completed");

injector.getInstance(P2PService.class).shutDown(() -> {
log.info("P2PService shutdown completed");
injector.getInstance(WalletsSetup.class).shutDownComplete.addListener((ov, o, n) -> {
log.info("WalletsSetup shutdown completed");
module.close(injector);
resultHandler.handleResult();
log.info("Graceful shutdown completed. Exiting now.");
System.exit(0);
});
injector.getInstance(WalletsSetup.class).shutDown();
injector.getInstance(BtcWalletService.class).shutDown();
injector.getInstance(BsqWalletService.class).shutDown();

module.close(injector);
resultHandler.handleResult();
log.info("Graceful shutdown completed. Exiting now.");
System.exit(0);
});
});
injector.getInstance(AvoidStandbyModeService.class).shutDown();
// we wait max 20 sec.
UserThread.runAfter(() -> {
log.warn("Timeout triggered resultHandler");
resultHandler.handleResult();
System.exit(0);
}, 20);
} else {
log.warn("injector == null triggered resultHandler");
UserThread.runAfter(() -> {
resultHandler.handleResult();
System.exit(0);
}, 1);
}
walletsSetup.shutDown();

});

// Wait max 20 sec.
UserThread.runAfter(() -> {
log.warn("Timeout triggered resultHandler");
resultHandler.handleResult();
System.exit(0);
}, 20);
} catch (Throwable t) {
log.error("App shutdown failed with exception");
log.error("App shutdown failed with exception {}", t.toString());
t.printStackTrace();
System.exit(1);
}
Expand Down
36 changes: 25 additions & 11 deletions core/src/main/java/bisq/core/btc/setup/WalletConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -492,8 +492,11 @@ void setPeerNodesForLocalHost() {
}
}

private Wallet createOrLoadWallet(File walletFile, boolean shouldReplayWallet,
BisqKeyChainGroup keyChainGroup, boolean isBsqWallet, DeterministicSeed restoreFromSeed)
private Wallet createOrLoadWallet(File walletFile,
boolean shouldReplayWallet,
BisqKeyChainGroup keyChainGroup,
boolean isBsqWallet,
DeterministicSeed restoreFromSeed)
throws Exception {

if (restoreFromSeed != null)
Expand Down Expand Up @@ -530,7 +533,9 @@ private void maybeMoveOldWalletOutOfTheWay(File vWalletFile) {
}
}

private Wallet loadWallet(File walletFile, boolean shouldReplayWallet, boolean useBitcoinDeterministicKeyChain) throws Exception {
private Wallet loadWallet(File walletFile,
boolean shouldReplayWallet,
boolean useBitcoinDeterministicKeyChain) throws Exception {
Wallet wallet;
try (FileInputStream walletStream = new FileInputStream(walletFile)) {
List<WalletExtension> extensions = provideWalletExtensions();
Expand Down Expand Up @@ -570,21 +575,30 @@ protected void shutDown() throws Exception {
// Runs in a separate thread.
try {
Context.propagate(context);
vPeerGroup.stop();

vBtcWallet.saveToFile(vBtcWalletFile);
if (vBsqWallet != null && vBsqWalletFile != null)
//noinspection ConstantConditions,ConstantConditions
vBtcWallet = null;
log.info("BtcWallet saved to file");

if (vBsqWallet != null && vBsqWalletFile != null) {
vBsqWallet.saveToFile(vBsqWalletFile);
vStore.close();
vBsqWallet = null;
log.info("BsqWallet saved to file");
}

vPeerGroup = null;
vBtcWallet = null;
vBsqWallet = null;
vStore.close();
vStore = null;
log.info("SPV file closed");

vChain = null;

// vPeerGroup.stop has no timeout and can take very long (10 sec. in my test). So we call it at the end.
// We might get likely interrupted by the parent call timeout.
vPeerGroup.stop();
vPeerGroup = null;
log.info("PeerGroup stopped");
} catch (BlockStoreException e) {
throw new IOException(e);
} catch (Throwable ignore) {
}
}

Expand Down
12 changes: 7 additions & 5 deletions core/src/main/java/bisq/core/btc/setup/WalletsSetup.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
package bisq.core.btc.setup;

import bisq.core.btc.exceptions.InvalidHostException;
import bisq.core.btc.nodes.LocalBitcoinNode;
import bisq.core.btc.exceptions.RejectedTxException;
import bisq.core.btc.model.AddressEntry;
import bisq.core.btc.model.AddressEntryList;
Expand All @@ -27,6 +26,7 @@
import bisq.core.btc.nodes.BtcNodes.BtcNode;
import bisq.core.btc.nodes.BtcNodesRepository;
import bisq.core.btc.nodes.BtcNodesSetupPreferences;
import bisq.core.btc.nodes.LocalBitcoinNode;
import bisq.core.user.Preferences;

import bisq.network.Socks5MultiDiscovery;
Expand Down Expand Up @@ -314,14 +314,16 @@ public void failed(@NotNull Service.State from, @NotNull Throwable failure) {
public void shutDown() {
if (walletConfig != null) {
try {
log.info("walletConfig shutDown started");
walletConfig.stopAsync();
walletConfig.awaitTerminated(5, TimeUnit.SECONDS);
walletConfig.awaitTerminated(1, TimeUnit.SECONDS);
log.info("walletConfig shutDown completed");
} catch (Throwable ignore) {
log.info("walletConfig shutDown interrupted by timeout");
}
shutDownComplete.set(true);
} else {
shutDownComplete.set(true);
}

shutDownComplete.set(true);
}

public void reSyncSPVChain() throws IOException {
Expand Down
13 changes: 10 additions & 3 deletions p2p/src/main/java/bisq/network/p2p/P2PService.java
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public class P2PService implements SetupListener, MessageListener, ConnectionLis
private final IntegerProperty numConnectedPeers = new SimpleIntegerProperty(0);

private volatile boolean shutDownInProgress;
@Getter
private boolean shutDownComplete;
private final Subscription networkReadySubscription;
private boolean isBootstrapped;
Expand Down Expand Up @@ -446,7 +447,9 @@ public void onAdded(Collection<ProtectedStorageEntry> protectedStorageEntries) {
}

@Override
public void onRemoved(Collection<ProtectedStorageEntry> protectedStorageEntries) { }
public void onRemoved(Collection<ProtectedStorageEntry> protectedStorageEntries) {
// not handled
}

///////////////////////////////////////////////////////////////////////////////////////////
// DirectMessages
Expand All @@ -463,7 +466,9 @@ public void sendEncryptedDirectMessage(NodeAddress peerNodeAddress, PubKeyRing p
}
}

private void doSendEncryptedDirectMessage(@NotNull NodeAddress peersNodeAddress, PubKeyRing pubKeyRing, NetworkEnvelope message,
private void doSendEncryptedDirectMessage(@NotNull NodeAddress peersNodeAddress,
PubKeyRing pubKeyRing,
NetworkEnvelope message,
SendDirectMessageListener sendDirectMessageListener) {
log.debug("Send encrypted direct message {} to peer {}",
message.getClass().getSimpleName(), peersNodeAddress);
Expand Down Expand Up @@ -691,7 +696,9 @@ public void onBroadcastedToFirstPeer(BroadcastMessage message) {
}

@Override
public void onBroadcastCompleted(BroadcastMessage message, int numOfCompletedBroadcasts, int numOfFailedBroadcasts) {
public void onBroadcastCompleted(BroadcastMessage message,
int numOfCompletedBroadcasts,
int numOfFailedBroadcasts) {
log.info("Broadcast completed: Sent to {} peers (failed: {}). Message = {}",
numOfCompletedBroadcasts, numOfFailedBroadcasts, Utilities.toTruncatedString(message));
if (numOfCompletedBroadcasts == 0)
Expand Down
11 changes: 11 additions & 0 deletions p2p/src/main/java/bisq/network/p2p/network/NetworkNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -334,14 +334,25 @@ public void shutDown(Runnable shutDownCompleteHandler) {

Set<Connection> allConnections = getAllConnections();
int numConnections = allConnections.size();

if (numConnections == 0) {
log.info("Shutdown immediately because no connections are open.");
if (shutDownCompleteHandler != null) {
shutDownCompleteHandler.run();
}
return;
}

log.info("Shutdown {} connections", numConnections);

AtomicInteger shutdownCompleted = new AtomicInteger();
Timer timeoutHandler = UserThread.runAfter(() -> {
if (shutDownCompleteHandler != null) {
log.info("Shutdown completed due timeout");
shutDownCompleteHandler.run();
}
}, 3);

allConnections.forEach(c -> c.shutDown(CloseConnectionReason.APP_SHUT_DOWN,
() -> {
shutdownCompleted.getAndIncrement();
Expand Down