synchronize P2PDataStorage to avoid race conditions

This commit is contained in:
woodser 2023-05-31 08:05:21 -04:00
parent 93a93d757c
commit 870630b381

View File

@ -232,8 +232,10 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
appendOnlyDataStoreService.readFromResources(postFix, () -> appendOnlyDataStoreServiceReady.set(true)); appendOnlyDataStoreService.readFromResources(postFix, () -> appendOnlyDataStoreServiceReady.set(true));
protectedDataStoreService.readFromResources(postFix, () -> { protectedDataStoreService.readFromResources(postFix, () -> {
synchronized (map) {
map.putAll(protectedDataStoreService.getMap()); map.putAll(protectedDataStoreService.getMap());
protectedDataStoreServiceReady.set(true); protectedDataStoreServiceReady.set(true);
}
}); });
resourceDataStoreService.readFromResources(postFix, () -> resourceDataStoreServiceReady.set(true)); resourceDataStoreService.readFromResources(postFix, () -> resourceDataStoreServiceReady.set(true));
} }
@ -241,21 +243,25 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
// Uses synchronous execution on the userThread. Only used by tests. The async methods should be used by app code. // Uses synchronous execution on the userThread. Only used by tests. The async methods should be used by app code.
@VisibleForTesting @VisibleForTesting
public void readFromResourcesSync(String postFix) { public void readFromResourcesSync(String postFix) {
synchronized (map) {
appendOnlyDataStoreService.readFromResourcesSync(postFix); appendOnlyDataStoreService.readFromResourcesSync(postFix);
protectedDataStoreService.readFromResourcesSync(postFix); protectedDataStoreService.readFromResourcesSync(postFix);
resourceDataStoreService.readFromResourcesSync(postFix); resourceDataStoreService.readFromResourcesSync(postFix);
map.putAll(protectedDataStoreService.getMap()); map.putAll(protectedDataStoreService.getMap());
} }
}
// We get added mailbox message data from MailboxMessageService. We want to add those early so we can get it added // We get added mailbox message data from MailboxMessageService. We want to add those early so we can get it added
// to our excluded keys to reduce initial data response data size. // to our excluded keys to reduce initial data response data size.
public void addProtectedMailboxStorageEntryToMap(ProtectedStorageEntry protectedStorageEntry) { public void addProtectedMailboxStorageEntryToMap(ProtectedStorageEntry protectedStorageEntry) {
synchronized (map) {
ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload(); ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload); ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload);
map.put(hashOfPayload, protectedStorageEntry); map.put(hashOfPayload, protectedStorageEntry);
//log.trace("## addProtectedMailboxStorageEntryToMap hashOfPayload={}, map={}", hashOfPayload, printMap()); //log.trace("## addProtectedMailboxStorageEntryToMap hashOfPayload={}, map={}", hashOfPayload, printMap());
} }
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// RequestData API // RequestData API
@ -627,6 +633,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
@VisibleForTesting @VisibleForTesting
void removeExpiredEntries() { void removeExpiredEntries() {
synchronized (map) {
// The moment when an object becomes expired will not be synchronous in the network and we could // The moment when an object becomes expired will not be synchronous in the network and we could
// get add network_messages after the object has expired. To avoid repeated additions of already expired // get add network_messages after the object has expired. To avoid repeated additions of already expired
// object when we get it sent from new peers, we dont remove the sequence number from the map. // object when we get it sent from new peers, we dont remove the sequence number from the map.
@ -651,6 +658,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
requestPersistence(); requestPersistence();
} }
} }
}
public void onBootstrapped() { public void onBootstrapped() {
removeExpiredEntriesTimer = UserThread.runPeriodically(this::removeExpiredEntries, CHECK_TTL_INTERVAL_SEC); removeExpiredEntriesTimer = UserThread.runPeriodically(this::removeExpiredEntries, CHECK_TTL_INTERVAL_SEC);
@ -699,6 +707,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
NodeAddress peersNodeAddress = connection.getPeersNodeAddressOptional().get(); NodeAddress peersNodeAddress = connection.getPeersNodeAddressOptional().get();
// Backdate all the eligible payloads based on the node that disconnected // Backdate all the eligible payloads based on the node that disconnected
synchronized (map) {
map.values().stream() map.values().stream()
.filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof RequiresOwnerIsOnlinePayload) .filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof RequiresOwnerIsOnlinePayload)
.filter(protectedStorageEntry -> ((RequiresOwnerIsOnlinePayload) protectedStorageEntry.getProtectedStoragePayload()).getOwnerNodeAddress().equals(peersNodeAddress)) .filter(protectedStorageEntry -> ((RequiresOwnerIsOnlinePayload) protectedStorageEntry.getProtectedStoragePayload()).getOwnerNodeAddress().equals(peersNodeAddress))
@ -716,6 +725,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
protectedStorageEntry.backDate(); protectedStorageEntry.backDate();
}); });
} }
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Client API // Client API
@ -818,6 +828,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
@Nullable NodeAddress sender, @Nullable NodeAddress sender,
@Nullable BroadcastHandler.Listener listener, @Nullable BroadcastHandler.Listener listener,
boolean allowBroadcast) { boolean allowBroadcast) {
synchronized (map) {
ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload(); ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload); ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload);
@ -894,6 +905,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
return true; return true;
} }
}
/** /**
* We do not do all checks as it is used for republishing existing mailbox messages from seed nodes which * We do not do all checks as it is used for republishing existing mailbox messages from seed nodes which
@ -935,7 +947,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
*/ */
public boolean refreshTTL(RefreshOfferMessage refreshTTLMessage, public boolean refreshTTL(RefreshOfferMessage refreshTTLMessage,
@Nullable NodeAddress sender) { @Nullable NodeAddress sender) {
synchronized (map) {
try { try {
ByteArray hashOfPayload = new ByteArray(refreshTTLMessage.getHashOfPayload()); ByteArray hashOfPayload = new ByteArray(refreshTTLMessage.getHashOfPayload());
ProtectedStorageEntry storedData = map.get(hashOfPayload); ProtectedStorageEntry storedData = map.get(hashOfPayload);
@ -980,6 +992,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
} }
return true; return true;
} }
}
/** /**
* Removes a ProtectedStorageEntry from the local P2P data storage. If it is successful, it will broadcast that * Removes a ProtectedStorageEntry from the local P2P data storage. If it is successful, it will broadcast that
@ -991,6 +1004,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
*/ */
public boolean remove(ProtectedStorageEntry protectedStorageEntry, public boolean remove(ProtectedStorageEntry protectedStorageEntry,
@Nullable NodeAddress sender) { @Nullable NodeAddress sender) {
synchronized (map) {
ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload(); ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload); ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload);
@ -1034,6 +1048,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
return true; return true;
} }
}
public ProtectedStorageEntry getProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload, public ProtectedStorageEntry getProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload,
KeyPair ownerStoragePubKey) KeyPair ownerStoragePubKey)
@ -1107,6 +1122,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
} }
private void removeFromMapAndDataStore(Collection<Map.Entry<ByteArray, ProtectedStorageEntry>> entriesToRemove) { private void removeFromMapAndDataStore(Collection<Map.Entry<ByteArray, ProtectedStorageEntry>> entriesToRemove) {
synchronized (map) {
if (entriesToRemove.isEmpty()) if (entriesToRemove.isEmpty())
return; return;
@ -1132,6 +1148,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
hashMapChangedListeners.forEach(e -> e.onRemoved(removedProtectedStorageEntries)); hashMapChangedListeners.forEach(e -> e.onRemoved(removedProtectedStorageEntries));
} }
}
private boolean hasSequenceNrIncreased(int newSequenceNumber, ByteArray hashOfData) { private boolean hasSequenceNrIncreased(int newSequenceNumber, ByteArray hashOfData) {
if (sequenceNumberMap.containsKey(hashOfData)) { if (sequenceNumberMap.containsKey(hashOfData)) {