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));
protectedDataStoreService.readFromResources(postFix, () -> {
synchronized (map) {
map.putAll(protectedDataStoreService.getMap());
protectedDataStoreServiceReady.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.
@VisibleForTesting
public void readFromResourcesSync(String postFix) {
synchronized (map) {
appendOnlyDataStoreService.readFromResourcesSync(postFix);
protectedDataStoreService.readFromResourcesSync(postFix);
resourceDataStoreService.readFromResourcesSync(postFix);
map.putAll(protectedDataStoreService.getMap());
}
}
// 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.
public void addProtectedMailboxStorageEntryToMap(ProtectedStorageEntry protectedStorageEntry) {
synchronized (map) {
ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload);
map.put(hashOfPayload, protectedStorageEntry);
//log.trace("## addProtectedMailboxStorageEntryToMap hashOfPayload={}, map={}", hashOfPayload, printMap());
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// RequestData API
@ -627,6 +633,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
@VisibleForTesting
void removeExpiredEntries() {
synchronized (map) {
// 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
// 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();
}
}
}
public void onBootstrapped() {
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();
// Backdate all the eligible payloads based on the node that disconnected
synchronized (map) {
map.values().stream()
.filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof RequiresOwnerIsOnlinePayload)
.filter(protectedStorageEntry -> ((RequiresOwnerIsOnlinePayload) protectedStorageEntry.getProtectedStoragePayload()).getOwnerNodeAddress().equals(peersNodeAddress))
@ -716,6 +725,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
protectedStorageEntry.backDate();
});
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// Client API
@ -818,6 +828,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
@Nullable NodeAddress sender,
@Nullable BroadcastHandler.Listener listener,
boolean allowBroadcast) {
synchronized (map) {
ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload);
@ -894,6 +905,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
return true;
}
}
/**
* 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,
@Nullable NodeAddress sender) {
synchronized (map) {
try {
ByteArray hashOfPayload = new ByteArray(refreshTTLMessage.getHashOfPayload());
ProtectedStorageEntry storedData = map.get(hashOfPayload);
@ -980,6 +992,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
}
return true;
}
}
/**
* 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,
@Nullable NodeAddress sender) {
synchronized (map) {
ProtectedStoragePayload protectedStoragePayload = protectedStorageEntry.getProtectedStoragePayload();
ByteArray hashOfPayload = get32ByteHashAsByteArray(protectedStoragePayload);
@ -1034,6 +1048,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
return true;
}
}
public ProtectedStorageEntry getProtectedStorageEntry(ProtectedStoragePayload protectedStoragePayload,
KeyPair ownerStoragePubKey)
@ -1107,6 +1122,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
}
private void removeFromMapAndDataStore(Collection<Map.Entry<ByteArray, ProtectedStorageEntry>> entriesToRemove) {
synchronized (map) {
if (entriesToRemove.isEmpty())
return;
@ -1132,6 +1148,7 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
hashMapChangedListeners.forEach(e -> e.onRemoved(removedProtectedStorageEntries));
}
}
private boolean hasSequenceNrIncreased(int newSequenceNumber, ByteArray hashOfData) {
if (sequenceNumberMap.containsKey(hashOfData)) {