mirror of
https://github.com/bisq-network/bisq.git
synced 2025-03-03 18:56:59 +01:00
Optimize startup performance
This commit is contained in:
parent
484b5411b9
commit
2fe77aadd4
3 changed files with 92 additions and 49 deletions
|
@ -64,14 +64,12 @@ import io.bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import io.bisq.core.user.DontShowAgainLookup;
|
import io.bisq.core.user.DontShowAgainLookup;
|
||||||
import io.bisq.core.user.Preferences;
|
import io.bisq.core.user.Preferences;
|
||||||
import io.bisq.core.user.User;
|
import io.bisq.core.user.User;
|
||||||
import io.bisq.gui.app.BisqApp;
|
|
||||||
import io.bisq.gui.common.model.ViewModel;
|
import io.bisq.gui.common.model.ViewModel;
|
||||||
import io.bisq.gui.components.BalanceWithConfirmationTextField;
|
import io.bisq.gui.components.BalanceWithConfirmationTextField;
|
||||||
import io.bisq.gui.components.TxIdTextField;
|
import io.bisq.gui.components.TxIdTextField;
|
||||||
import io.bisq.gui.main.overlays.notifications.NotificationCenter;
|
import io.bisq.gui.main.overlays.notifications.NotificationCenter;
|
||||||
import io.bisq.gui.main.overlays.popups.Popup;
|
import io.bisq.gui.main.overlays.popups.Popup;
|
||||||
import io.bisq.gui.main.overlays.windows.DisplayAlertMessageWindow;
|
import io.bisq.gui.main.overlays.windows.DisplayAlertMessageWindow;
|
||||||
import io.bisq.gui.main.overlays.windows.SelectBaseCurrencyWindow;
|
|
||||||
import io.bisq.gui.main.overlays.windows.TacWindow;
|
import io.bisq.gui.main.overlays.windows.TacWindow;
|
||||||
import io.bisq.gui.main.overlays.windows.WalletPasswordWindow;
|
import io.bisq.gui.main.overlays.windows.WalletPasswordWindow;
|
||||||
import io.bisq.gui.main.overlays.windows.downloadupdate.DisplayUpdateDownloadWindow;
|
import io.bisq.gui.main.overlays.windows.downloadupdate.DisplayUpdateDownloadWindow;
|
||||||
|
@ -192,6 +190,7 @@ public class MainViewModel implements ViewModel {
|
||||||
private BooleanProperty p2pNetWorkReady;
|
private BooleanProperty p2pNetWorkReady;
|
||||||
private final BooleanProperty walletInitialized = new SimpleBooleanProperty();
|
private final BooleanProperty walletInitialized = new SimpleBooleanProperty();
|
||||||
private boolean allBasicServicesInitialized;
|
private boolean allBasicServicesInitialized;
|
||||||
|
private BooleanProperty loadEntryMapResult, checkCryptoSetupResult;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -255,10 +254,6 @@ public class MainViewModel implements ViewModel {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void start() {
|
public void start() {
|
||||||
checkCryptoSetup();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showTacWindow() {
|
|
||||||
//noinspection ConstantConditions,ConstantConditions
|
//noinspection ConstantConditions,ConstantConditions
|
||||||
if (!preferences.isTacAccepted() && !DevEnv.DEV_MODE) {
|
if (!preferences.isTacAccepted() && !DevEnv.DEV_MODE) {
|
||||||
UserThread.runAfter(() -> {
|
UserThread.runAfter(() -> {
|
||||||
|
@ -266,7 +261,7 @@ public class MainViewModel implements ViewModel {
|
||||||
preferences.setTacAccepted(true);
|
preferences.setTacAccepted(true);
|
||||||
showSelectBaseCurrencyWindow();
|
showSelectBaseCurrencyWindow();
|
||||||
}).show();
|
}).show();
|
||||||
}, 2);
|
}, 1);
|
||||||
} else {
|
} else {
|
||||||
showSelectBaseCurrencyWindow();
|
showSelectBaseCurrencyWindow();
|
||||||
}
|
}
|
||||||
|
@ -275,7 +270,11 @@ public class MainViewModel implements ViewModel {
|
||||||
private void showSelectBaseCurrencyWindow() {
|
private void showSelectBaseCurrencyWindow() {
|
||||||
String key = "showSelectBaseCurrencyWindowAtFistStartup";
|
String key = "showSelectBaseCurrencyWindowAtFistStartup";
|
||||||
if (preferences.showAgain(key)) {
|
if (preferences.showAgain(key)) {
|
||||||
new SelectBaseCurrencyWindow()
|
bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork());
|
||||||
|
preferences.dontShowAgain(key, true);
|
||||||
|
showRevertIdCheckRequirement();
|
||||||
|
|
||||||
|
/* new SelectBaseCurrencyWindow()
|
||||||
.onSelect(baseCurrencyNetwork -> {
|
.onSelect(baseCurrencyNetwork -> {
|
||||||
preferences.dontShowAgain(key, true);
|
preferences.dontShowAgain(key, true);
|
||||||
final boolean hasChanged = !BisqEnvironment.getBaseCurrencyNetwork().equals(baseCurrencyNetwork);
|
final boolean hasChanged = !BisqEnvironment.getBaseCurrencyNetwork().equals(baseCurrencyNetwork);
|
||||||
|
@ -294,49 +293,30 @@ public class MainViewModel implements ViewModel {
|
||||||
.onAction(() -> {
|
.onAction(() -> {
|
||||||
bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork());
|
bisqEnvironment.saveBaseCryptoNetwork(BisqEnvironment.getBaseCurrencyNetwork());
|
||||||
preferences.dontShowAgain(key, true);
|
preferences.dontShowAgain(key, true);
|
||||||
checkIfLocalHostNodeIsRunning();
|
showRevertIdCheckRequirement();
|
||||||
})
|
})
|
||||||
.hideCloseButton()
|
.hideCloseButton()
|
||||||
.show();
|
.show();*/
|
||||||
} else {
|
} else {
|
||||||
checkIfLocalHostNodeIsRunning();
|
showRevertIdCheckRequirement();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkIfLocalHostNodeIsRunning() {
|
private void showRevertIdCheckRequirement() {
|
||||||
Socket socket = null;
|
//TODO remove after v0.5.2
|
||||||
try {
|
String key = "revertIdCheckRequirement";
|
||||||
socket = new Socket();
|
if (preferences.showAgain(key) && Version.VERSION.equals("0.5.2") &&
|
||||||
socket.connect(new InetSocketAddress(InetAddresses.forString("127.0.0.1"),
|
user.getPaymentAccounts() != null && !user.getPaymentAccounts().isEmpty())
|
||||||
BisqEnvironment.getBaseCurrencyNetwork().getParameters().getPort()), 5000);
|
new Popup<>().information(Res.get("popup.info.revertIdCheckRequirement")).show();
|
||||||
log.info("Localhost peer detected.");
|
preferences.dontShowAgain(key, true);
|
||||||
bisqEnvironment.setBitcoinLocalhostNodeRunning(true);
|
checkIfLocalHostNodeIsRunning();
|
||||||
} catch (IOException e) {
|
|
||||||
log.info("Localhost peer not detected.");
|
|
||||||
} finally {
|
|
||||||
if (socket != null) {
|
|
||||||
try {
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException ignore) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startBasicServices();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startBasicServices() {
|
private void startBasicServices() {
|
||||||
log.info("startBasicServices");
|
log.info("startBasicServices");
|
||||||
|
|
||||||
//TODO remove after v0.5.2
|
loadEntryMapResult = loadEntryMap();
|
||||||
String key = "revertIdCheckRequirement";
|
checkCryptoSetupResult = checkCryptoSetup();
|
||||||
if (preferences.showAgain(key) && Version.VERSION.equals("0.5.2")) {
|
|
||||||
new Popup<>().information(Res.get("popup.info.revertIdCheckRequirement")).show();
|
|
||||||
preferences.dontShowAgain(key, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...)
|
|
||||||
final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode();
|
|
||||||
p2PService.readEntryMapFromResources(storageFileName);
|
|
||||||
|
|
||||||
ChangeListener<Boolean> walletInitializedListener = (observable, oldValue, newValue) -> {
|
ChangeListener<Boolean> walletInitializedListener = (observable, oldValue, newValue) -> {
|
||||||
if (newValue && !p2pNetWorkReady.get())
|
if (newValue && !p2pNetWorkReady.get())
|
||||||
|
@ -359,7 +339,15 @@ public class MainViewModel implements ViewModel {
|
||||||
initWalletService();
|
initWalletService();
|
||||||
|
|
||||||
// need to store it to not get garbage collected
|
// need to store it to not get garbage collected
|
||||||
allServicesDone = EasyBind.combine(walletInitialized, p2pNetWorkReady, (a, b) -> a && b);
|
allServicesDone = EasyBind.combine(checkCryptoSetupResult, loadEntryMapResult, walletInitialized, p2pNetWorkReady,
|
||||||
|
(a, b, c, d) -> {
|
||||||
|
log.info("\ncheckCryptoSetupResult={}\n" +
|
||||||
|
"loadEntryMapResult={}\n" +
|
||||||
|
"walletInitialized={}\n" +
|
||||||
|
"p2pNetWorkReady={}",
|
||||||
|
a, b, c, d);
|
||||||
|
return a && b && c && d;
|
||||||
|
});
|
||||||
allServicesDone.subscribe((observable, oldValue, newValue) -> {
|
allServicesDone.subscribe((observable, oldValue, newValue) -> {
|
||||||
if (newValue) {
|
if (newValue) {
|
||||||
startupTimeout.stop();
|
startupTimeout.stop();
|
||||||
|
@ -393,6 +381,22 @@ public class MainViewModel implements ViewModel {
|
||||||
// Initialisation
|
// Initialisation
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
private BooleanProperty loadEntryMap() {
|
||||||
|
BooleanProperty result = new SimpleBooleanProperty();
|
||||||
|
Thread loadEntryMapThread = new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Thread.currentThread().setName("loadEntryMapThread");
|
||||||
|
// Used to load different EntryMap files per base currency (EntryMap_BTC, EntryMap_LTC,...)
|
||||||
|
final String storageFileName = "EntryMap_" + BisqEnvironment.getBaseCurrencyNetwork().getCurrencyCode();
|
||||||
|
p2PService.readEntryMapFromResources(storageFileName);
|
||||||
|
UserThread.execute(() -> result.set(true));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadEntryMapThread.start();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private BooleanProperty initP2PNetwork() {
|
private BooleanProperty initP2PNetwork() {
|
||||||
log.info("initP2PNetwork");
|
log.info("initP2PNetwork");
|
||||||
|
|
||||||
|
@ -727,7 +731,39 @@ public class MainViewModel implements ViewModel {
|
||||||
.show();
|
.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkCryptoSetup() {
|
private void checkIfLocalHostNodeIsRunning() {
|
||||||
|
Thread checkIfLocalHostNodeIsRunningThread = new Thread() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Thread.currentThread().setName("checkIfLocalHostNodeIsRunningThread");
|
||||||
|
Socket socket = null;
|
||||||
|
try {
|
||||||
|
socket = new Socket();
|
||||||
|
socket.connect(new InetSocketAddress(InetAddresses.forString("127.0.0.1"),
|
||||||
|
BisqEnvironment.getBaseCurrencyNetwork().getParameters().getPort()), 5000);
|
||||||
|
log.info("Localhost peer detected.");
|
||||||
|
UserThread.execute(() -> {
|
||||||
|
bisqEnvironment.setBitcoinLocalhostNodeRunning(true);
|
||||||
|
startBasicServices();
|
||||||
|
});
|
||||||
|
} catch (Throwable e) {
|
||||||
|
log.info("Localhost peer not detected.");
|
||||||
|
UserThread.execute(MainViewModel.this::startBasicServices);
|
||||||
|
} finally {
|
||||||
|
if (socket != null) {
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
} catch (IOException ignore) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
checkIfLocalHostNodeIsRunningThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
private BooleanProperty checkCryptoSetup() {
|
||||||
|
BooleanProperty result = new SimpleBooleanProperty();
|
||||||
// We want to test if the client is compiled with the correct crypto provider (BountyCastle)
|
// We want to test if the client is compiled with the correct crypto provider (BountyCastle)
|
||||||
// and if the unlimited Strength for cryptographic keys is set.
|
// and if the unlimited Strength for cryptographic keys is set.
|
||||||
// If users compile themselves they might miss that step and then would get an exception in the trade.
|
// If users compile themselves they might miss that step and then would get an exception in the trade.
|
||||||
|
@ -750,7 +786,7 @@ public class MainViewModel implements ViewModel {
|
||||||
log.debug("Crypto test succeeded");
|
log.debug("Crypto test succeeded");
|
||||||
|
|
||||||
if (Security.getProvider("BC") != null) {
|
if (Security.getProvider("BC") != null) {
|
||||||
UserThread.execute(MainViewModel.this::showTacWindow);
|
UserThread.execute(() -> result.set(true));
|
||||||
} else {
|
} else {
|
||||||
throw new CryptoException("Security provider BountyCastle is not available.");
|
throw new CryptoException("Security provider BountyCastle is not available.");
|
||||||
}
|
}
|
||||||
|
@ -769,6 +805,8 @@ public class MainViewModel implements ViewModel {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
checkCryptoThread.start();
|
checkCryptoThread.start();
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkIfOpenOffersMatchTradeProtocolVersion() {
|
private void checkIfOpenOffersMatchTradeProtocolVersion() {
|
||||||
|
|
|
@ -97,11 +97,9 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||||
// We get it called in readPersistedEntryMap once ready
|
// We get it called in readPersistedEntryMap once ready
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readEntryMapFromResources(String resourceFileName) {
|
// This method is called at startup in a non-user thread.
|
||||||
SequenceNumberMap persistedSequenceNumberMap = sequenceNumberMapStorage.initAndGetPersisted(sequenceNumberMap);
|
// We should not have any threading issues here as the p2p network is just initializing
|
||||||
if (persistedSequenceNumberMap != null)
|
public synchronized void readEntryMapFromResources(String resourceFileName) {
|
||||||
sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persistedSequenceNumberMap.getMap()));
|
|
||||||
|
|
||||||
final String storageFileName = "EntryMap";
|
final String storageFileName = "EntryMap";
|
||||||
File dbDir = new File(storageDir.getAbsolutePath());
|
File dbDir = new File(storageDir.getAbsolutePath());
|
||||||
if (!dbDir.exists() && !dbDir.mkdir())
|
if (!dbDir.exists() && !dbDir.mkdir())
|
||||||
|
@ -110,6 +108,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||||
final File destinationFile = new File(Paths.get(storageDir.getAbsolutePath(), storageFileName).toString());
|
final File destinationFile = new File(Paths.get(storageDir.getAbsolutePath(), storageFileName).toString());
|
||||||
if (!destinationFile.exists()) {
|
if (!destinationFile.exists()) {
|
||||||
try {
|
try {
|
||||||
|
log.info("We copy resource to file: resourceFileName={}, destinationFile={}", resourceFileName, destinationFile);
|
||||||
FileUtil.resourceToFile(resourceFileName, destinationFile);
|
FileUtil.resourceToFile(resourceFileName, destinationFile);
|
||||||
} catch (ResourceNotFoundException e) {
|
} catch (ResourceNotFoundException e) {
|
||||||
log.info("Could not find resourceFile " + resourceFileName + ". That is expected if none is provided yet.");
|
log.info("Could not find resourceFile " + resourceFileName + ". That is expected if none is provided yet.");
|
||||||
|
@ -121,8 +120,9 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
|
||||||
} else {
|
} else {
|
||||||
log.debug(storageFileName + " file exists already.");
|
log.debug(storageFileName + " file exists already.");
|
||||||
}
|
}
|
||||||
|
// takes about 4 seconds with PB! :-(
|
||||||
persistedEntryMap = persistedEntryMapStorage.<HashMap<ByteArray, MapValue>>initAndGetPersistedWithFileName(storageFileName);
|
persistedEntryMap = persistedEntryMapStorage.<HashMap<ByteArray, MapValue>>initAndGetPersistedWithFileName(storageFileName);
|
||||||
|
|
||||||
if (persistedEntryMap != null) {
|
if (persistedEntryMap != null) {
|
||||||
map.putAll(persistedEntryMap.getMap());
|
map.putAll(persistedEntryMap.getMap());
|
||||||
log.info("persistedEntryMap size=" + map.size());
|
log.info("persistedEntryMap size=" + map.size());
|
||||||
|
|
|
@ -23,12 +23,14 @@ import io.bisq.common.proto.persistable.PersistableEnvelope;
|
||||||
import io.bisq.generated.protobuffer.PB;
|
import io.bisq.generated.protobuffer.PB;
|
||||||
import io.bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
import io.bisq.network.p2p.storage.payload.ProtectedStorageEntry;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
public class PersistedEntryMap implements PersistableEnvelope {
|
public class PersistedEntryMap implements PersistableEnvelope {
|
||||||
@Getter
|
@Getter
|
||||||
private Map<P2PDataStorage.ByteArray, ProtectedStorageEntry> map = new ConcurrentHashMap<>();
|
private Map<P2PDataStorage.ByteArray, ProtectedStorageEntry> map = new ConcurrentHashMap<>();
|
||||||
|
@ -54,6 +56,9 @@ public class PersistedEntryMap implements PersistableEnvelope {
|
||||||
public static PersistableEnvelope fromProto(Map<String, PB.ProtectedStorageEntry> proto,
|
public static PersistableEnvelope fromProto(Map<String, PB.ProtectedStorageEntry> proto,
|
||||||
NetworkProtoResolver networkProtoResolver) {
|
NetworkProtoResolver networkProtoResolver) {
|
||||||
// Protobuffer maps don't support bytes as key so we use a hex string
|
// Protobuffer maps don't support bytes as key so we use a hex string
|
||||||
|
|
||||||
|
// Takes about 4 sec for 4000 items ;-( Java serialisation was 500 ms
|
||||||
|
log.info("PersistedEntryMap.fromProto size: " + proto.entrySet().size());
|
||||||
Map<P2PDataStorage.ByteArray, ProtectedStorageEntry> map = proto.entrySet().stream()
|
Map<P2PDataStorage.ByteArray, ProtectedStorageEntry> map = proto.entrySet().stream()
|
||||||
.collect(Collectors.<Map.Entry<String, PB.ProtectedStorageEntry>, P2PDataStorage.ByteArray, ProtectedStorageEntry>toMap(
|
.collect(Collectors.<Map.Entry<String, PB.ProtectedStorageEntry>, P2PDataStorage.ByteArray, ProtectedStorageEntry>toMap(
|
||||||
e -> new P2PDataStorage.ByteArray(e.getKey()),
|
e -> new P2PDataStorage.ByteArray(e.getKey()),
|
||||||
|
|
Loading…
Add table
Reference in a new issue