diff --git a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java index 2c86f0eb6c8..904a54eeb77 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/P2PDataStorage.java @@ -92,7 +92,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Optional; +import java.util.NoSuchElementException; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; @@ -213,6 +213,13 @@ public GetUpdatedDataRequest buildGetUpdatedDataRequest(NodeAddress senderNodeAd return new GetUpdatedDataRequest(senderNodeAddress, nonce, this.getKnownPayloadHashes()); } + /** + * Create the special key.

+ * + * For example: "1.3.4" encoded into a 20 byte array. + * + * @return the special key + */ private byte[] getSpecialKey() { byte[] result = new byte[20]; Arrays.fill(result, (byte) 0); @@ -220,12 +227,19 @@ private byte[] getSpecialKey() { return result; } - private String containsSpecialKey(Set collection) { - Optional result = collection.stream().map(byteArray -> new String(byteArray.bytes).trim()).filter(s -> s.matches("^[0-9]\\.[0-9]\\.[0-9]$")).findFirst(); - if (result.isPresent()) - return result.get(); - else - return ""; + /** + * See if the request contains a "special key". + * + * @param knownPayloadHashes + * @throws NoSuchElementException if there is no "special key" in the list + * @return the "special key" + */ + private String containsSpecialKey(Set knownPayloadHashes) { + return knownPayloadHashes.stream() + .map(byteArray -> new String(byteArray.bytes).trim()) + .filter(s -> s.matches("^[0-9]\\.[0-9]\\.[0-9]$")) + .findFirst() + .orElseThrow(); } /** @@ -238,14 +252,15 @@ private Set getKnownPayloadHashes() { // an object gets removed in between PreliminaryGetDataRequest and the GetUpdatedDataRequest and we would // miss that event if we do not load the full set or use some delta handling. Set excludedKeys; - if (seedNodeRepository != null && seedNodeRepository.isSeedNode(networkNode.getNodeAddress())) + if (seedNodeRepository != null && seedNodeRepository.isSeedNode(networkNode.getNodeAddress())) { excludedKeys = this.appendOnlyDataStoreService.getMap().keySet().stream() .map(e -> e.bytes) .collect(Collectors.toSet()); - else + } else { excludedKeys = this.appendOnlyDataStoreService.getMap("since " + Version.VERSION).keySet().stream() .map(e -> e.bytes) .collect(Collectors.toSet()); + } Set excludedKeysFromPersistedEntryMap = this.map.keySet() .stream() @@ -299,18 +314,19 @@ public GetDataResponse buildGetDataResponse( Set excludedKeysAsByteArray = P2PDataStorage.ByteArray.convertBytesSetToByteArraySet(getDataRequest.getExcludedKeys()); - String specialKey = containsSpecialKey(excludedKeysAsByteArray); - - Map tmp; - if ("".equals(specialKey)) - tmp = this.appendOnlyDataStoreService.getMap(); - else { - tmp = this.appendOnlyDataStoreService.getMap("since " + specialKey); + // In case we get a "new" data request, ie. with a "special key" like "1.3.4", we + // pre-filter the data. If there is no "special key", we use all data. + Map prefilteredData; + try { + String specialKey = containsSpecialKey(excludedKeysAsByteArray); + prefilteredData = this.appendOnlyDataStoreService.getMap("since " + specialKey); + } catch (NoSuchElementException e) { + prefilteredData = this.appendOnlyDataStoreService.getMap(); } Set filteredPersistableNetworkPayloads = filterKnownHashes( - tmp, + prefilteredData, Function.identity(), excludedKeysAsByteArray, peerCapabilities, diff --git a/p2p/src/main/java/bisq/network/p2p/storage/persistence/SplitStoreService.java b/p2p/src/main/java/bisq/network/p2p/storage/persistence/SplitStoreService.java index 0036c03e2e6..a67ea65a126 100644 --- a/p2p/src/main/java/bisq/network/p2p/storage/persistence/SplitStoreService.java +++ b/p2p/src/main/java/bisq/network/p2p/storage/persistence/SplitStoreService.java @@ -100,32 +100,72 @@ public Map getMap(String fi filter = filter.replace("since ", ""); if (!filter.equals(Version.VERSION)) { String finalFilter = filter; - history.entrySet().stream().filter(entry -> Integer.valueOf(entry.getKey().replace(".", "")) > Integer.valueOf(finalFilter.replace(".", ""))).forEach(entry -> result.putAll(entry.getValue().getMap())); + history.entrySet().stream() + .filter(entry -> parseSpecialKey(entry.getKey()) > parseSpecialKey(finalFilter)) + .forEach(entry -> result.putAll(entry.getValue().getMap())); } } return result; } + private int parseSpecialKey(String specialKey) { + return Integer.parseInt(specialKey.replace(".", "")); + } + + /** + * For the {@link SplitStoreService}s, we check if we already have all the historical data stores in our working + * directory. If we have, we can proceed loading the stores. If we do not, we have to transfer the fresh data stores + * from resources. + * + * @param postFix + */ @Override protected void readFromResources(String postFix) { // check Version.VERSION and see if we have latest r/o data store file in working directory - if (!new File(absolutePathOfStorageDir + File.separator + getFileName() + "_" + Version.VERSION).exists()) + if (!new File(absolutePathOfStorageDir, getFileName() + "_" + Version.VERSION).exists()) makeFileFromResourceFile(postFix); // if we have the latest file, we are good, else do stuff // TODO are we? else { - // load stores/storage + // if we have the r/o data stores in our working directory already, we can proceed on loading them. File dbDir = new File(absolutePathOfStorageDir); - List resourceFiles = Arrays.asList(dbDir.list((dir, name) -> name.startsWith(getFileName() + "_"))).stream().map(s -> new File(s)).collect(Collectors.toList()); + List resourceFiles = Arrays.asList(dbDir.list((dir, name) -> name.startsWith(getFileName() + "_"))) + .stream() + .map(s -> new File(s)) + .collect(Collectors.toList()); history = new HashMap<>(); store = readStore(getFileName()); resourceFiles.forEach(file -> { SplitStore tmp = readStore(file.getName().replace(postFix, "")); - history.put(file.getName().replace(postFix, "").replace(getFileName(), "").replace("_", ""), tmp); + history.put(extractSpecialKey(file.getName(), postFix), tmp); }); } } + /** + * Extracts the special key from a file name.

+ * + * Example: "TradeStatistics2Store_1.3.4_BTC_MAINNET" becomes "1.3.4" + * + * @param name the file name to begin with + * @param postFix the postfix eg. "BTC_MAINNET" + * @return the special key eg. "1.3.4" + */ + private String extractSpecialKey(String name, String postFix) { + return name.replace(postFix, "") // remove the postfix + .replace("_", "") // remove the spacings + .replace(getFileName(), ""); // remove the file name + } + + /** + * Bluntly copy and pasted from {@link StoreService} because: + *
  • The member function there is private
  • + *
  • it does not match our interface
  • + *
  • is a temporary solutions, until https://github.com/bisq-network/projects/issues/29
+ * + * @param name + * @return store + */ private T readStore(String name) { T store = storage.initAndGetPersistedWithFileName(name, 100); if (store != null) { @@ -139,6 +179,15 @@ private T readStore(String name) { return store; } + /** + * We compile a list of files available in resources and:
+ *
  • copy them to our working directory
  • + *
  • load the freshly copied stores
  • + *
  • remove the contents from our live data store
  • + *
  • persist the cleaned-up live store
+ * + * @param postFix + */ @Override protected void makeFileFromResourceFile(String postFix) { File dbDir = new File(absolutePathOfStorageDir); @@ -149,25 +198,25 @@ protected void makeFileFromResourceFile(String postFix) { List versions = new ArrayList<>(); versions.add(Version.VERSION); versions.addAll(Version.history); - List resourceFiles = versions.stream().map(s -> getFileName() + "_" + s + postFix).collect(Collectors.toList()); + List resourceFiles = versions.stream() + .map(s -> getFileName() + "_" + s + postFix) + .collect(Collectors.toList()); // if not, copy and split - resourceFiles.forEach(file -> { - final File destinationFile = new File(Paths.get(absolutePathOfStorageDir, file.replace(postFix, "")).toString()); - final String resourceFileName = file; + resourceFiles.forEach(resourceFileName -> { + final File destinationFile = new File(absolutePathOfStorageDir, resourceFileName.replace(postFix, "")); if (!destinationFile.exists()) { try { log.info("We copy resource to file: resourceFileName={}, destinationFile={}", resourceFileName, destinationFile); FileUtil.resourceToFile(resourceFileName, destinationFile); } catch (ResourceNotFoundException e) { - log.info("Could not find resourceFile " + resourceFileName + ". That is expected if none is provided yet."); + log.info("Could not find resourceFile {}. That is expected if none is provided yet.", resourceFileName); } catch (Throwable e) { - log.error("Could not copy resourceFile " + resourceFileName + " to " + - destinationFile.getAbsolutePath() + ".\n" + e.getMessage()); + log.error("Could not copy resourceFile {} to {}.\n{}", resourceFileName, destinationFile.getAbsolutePath(), e.getMessage()); e.printStackTrace(); } } else { - log.debug(file + " file exists already."); + log.debug(resourceFileName + " file exists already."); } }); @@ -177,7 +226,7 @@ protected void makeFileFromResourceFile(String postFix) { store = readStore(getFileName()); resourceFiles.forEach(file -> { SplitStore tmp = readStore(file.replace(postFix, "")); - history.put(file.replace(postFix, "").replace(getFileName(), "").replace("_", ""), tmp); + history.put(extractSpecialKey(file, postFix), tmp); // - subtract all that is in resource files store.getMap().keySet().removeAll(tmp.getMap().keySet()); });