fix concurrent modification in portfolio by locking sequence number map

This commit is contained in:
woodser 2025-04-28 06:40:17 -04:00 committed by woodser
parent 81eaeb6df0
commit c214919aa5
2 changed files with 49 additions and 23 deletions

View file

@ -187,7 +187,9 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
@Override @Override
public void readPersisted(Runnable completeHandler) { public void readPersisted(Runnable completeHandler) {
persistenceManager.readPersisted(persisted -> { persistenceManager.readPersisted(persisted -> {
synchronized (persisted.getMap()) {
sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap())); sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap()));
}
completeHandler.run(); completeHandler.run();
}, },
completeHandler); completeHandler);
@ -198,9 +200,11 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
public void readPersistedSync() { public void readPersistedSync() {
SequenceNumberMap persisted = persistenceManager.getPersisted(); SequenceNumberMap persisted = persistenceManager.getPersisted();
if (persisted != null) { if (persisted != null) {
synchronized (persisted.getMap()) {
sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap())); sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap()));
} }
} }
}
// Threading is done on the persistenceManager level // Threading is done on the persistenceManager level
public void readFromResources(String postFix, Runnable completeHandler) { public void readFromResources(String postFix, Runnable completeHandler) {
@ -641,12 +645,14 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
} }
removeFromMapAndDataStore(toRemoveList); removeFromMapAndDataStore(toRemoveList);
synchronized (sequenceNumberMap.getMap()) {
if (sequenceNumberMap.size() > this.maxSequenceNumberMapSizeBeforePurge) { if (sequenceNumberMap.size() > this.maxSequenceNumberMapSizeBeforePurge) {
sequenceNumberMap.setMap(getPurgedSequenceNumberMap(sequenceNumberMap.getMap())); sequenceNumberMap.setMap(getPurgedSequenceNumberMap(sequenceNumberMap.getMap()));
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);

View file

@ -19,8 +19,6 @@ package haveno.network.p2p.storage.persistence;
import haveno.common.proto.persistable.PersistableEnvelope; import haveno.common.proto.persistable.PersistableEnvelope;
import haveno.network.p2p.storage.P2PDataStorage; import haveno.network.p2p.storage.P2PDataStorage;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -33,8 +31,6 @@ import java.util.stream.Collectors;
* Hence this Persistable class. * Hence this Persistable class.
*/ */
public class SequenceNumberMap implements PersistableEnvelope { public class SequenceNumberMap implements PersistableEnvelope {
@Getter
@Setter
private Map<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map = new ConcurrentHashMap<>(); private Map<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map = new ConcurrentHashMap<>();
public SequenceNumberMap() { public SequenceNumberMap() {
@ -46,11 +42,14 @@ public class SequenceNumberMap implements PersistableEnvelope {
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private SequenceNumberMap(Map<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map) { private SequenceNumberMap(Map<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map) {
synchronized (this.map) {
this.map.putAll(map); this.map.putAll(map);
} }
}
@Override @Override
public protobuf.PersistableEnvelope toProtoMessage() { public protobuf.PersistableEnvelope toProtoMessage() {
synchronized (map) {
return protobuf.PersistableEnvelope.newBuilder() return protobuf.PersistableEnvelope.newBuilder()
.setSequenceNumberMap(protobuf.SequenceNumberMap.newBuilder() .setSequenceNumberMap(protobuf.SequenceNumberMap.newBuilder()
.addAllSequenceNumberEntries(map.entrySet().stream() .addAllSequenceNumberEntries(map.entrySet().stream()
@ -61,6 +60,7 @@ public class SequenceNumberMap implements PersistableEnvelope {
.collect(Collectors.toList()))) .collect(Collectors.toList())))
.build(); .build();
} }
}
public static SequenceNumberMap fromProto(protobuf.SequenceNumberMap proto) { public static SequenceNumberMap fromProto(protobuf.SequenceNumberMap proto) {
HashMap<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map = new HashMap<>(); HashMap<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map = new HashMap<>();
@ -74,20 +74,40 @@ public class SequenceNumberMap implements PersistableEnvelope {
// API // API
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public Map<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> getMap() {
synchronized (map) {
return map;
}
}
public void setMap(Map<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map) {
synchronized (this.map) {
this.map = map;
}
}
// Delegates // Delegates
public int size() { public int size() {
synchronized (map) {
return map.size(); return map.size();
} }
}
public boolean containsKey(P2PDataStorage.ByteArray key) { public boolean containsKey(P2PDataStorage.ByteArray key) {
synchronized (map) {
return map.containsKey(key); return map.containsKey(key);
} }
}
public P2PDataStorage.MapValue get(P2PDataStorage.ByteArray key) { public P2PDataStorage.MapValue get(P2PDataStorage.ByteArray key) {
synchronized (map) {
return map.get(key); return map.get(key);
} }
}
public void put(P2PDataStorage.ByteArray key, P2PDataStorage.MapValue value) { public void put(P2PDataStorage.ByteArray key, P2PDataStorage.MapValue value) {
synchronized (map) {
map.put(key, value); map.put(key, value);
} }
}
} }