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
public void readPersisted(Runnable completeHandler) {
persistenceManager.readPersisted(persisted -> {
synchronized (persisted.getMap()) {
sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap()));
}
completeHandler.run();
},
completeHandler);
@ -198,9 +200,11 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
public void readPersistedSync() {
SequenceNumberMap persisted = persistenceManager.getPersisted();
if (persisted != null) {
synchronized (persisted.getMap()) {
sequenceNumberMap.setMap(getPurgedSequenceNumberMap(persisted.getMap()));
}
}
}
// Threading is done on the persistenceManager level
public void readFromResources(String postFix, Runnable completeHandler) {
@ -641,12 +645,14 @@ public class P2PDataStorage implements MessageListener, ConnectionListener, Pers
}
removeFromMapAndDataStore(toRemoveList);
synchronized (sequenceNumberMap.getMap()) {
if (sequenceNumberMap.size() > this.maxSequenceNumberMapSizeBeforePurge) {
sequenceNumberMap.setMap(getPurgedSequenceNumberMap(sequenceNumberMap.getMap()));
requestPersistence();
}
}
}
}
public void onBootstrapped() {
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.network.p2p.storage.P2PDataStorage;
import lombok.Getter;
import lombok.Setter;
import java.util.HashMap;
import java.util.Map;
@ -33,8 +31,6 @@ import java.util.stream.Collectors;
* Hence this Persistable class.
*/
public class SequenceNumberMap implements PersistableEnvelope {
@Getter
@Setter
private Map<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map = new ConcurrentHashMap<>();
public SequenceNumberMap() {
@ -46,11 +42,14 @@ public class SequenceNumberMap implements PersistableEnvelope {
///////////////////////////////////////////////////////////////////////////////////////////
private SequenceNumberMap(Map<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map) {
synchronized (this.map) {
this.map.putAll(map);
}
}
@Override
public protobuf.PersistableEnvelope toProtoMessage() {
synchronized (map) {
return protobuf.PersistableEnvelope.newBuilder()
.setSequenceNumberMap(protobuf.SequenceNumberMap.newBuilder()
.addAllSequenceNumberEntries(map.entrySet().stream()
@ -61,6 +60,7 @@ public class SequenceNumberMap implements PersistableEnvelope {
.collect(Collectors.toList())))
.build();
}
}
public static SequenceNumberMap fromProto(protobuf.SequenceNumberMap proto) {
HashMap<P2PDataStorage.ByteArray, P2PDataStorage.MapValue> map = new HashMap<>();
@ -74,20 +74,40 @@ public class SequenceNumberMap implements PersistableEnvelope {
// 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
public int size() {
synchronized (map) {
return map.size();
}
}
public boolean containsKey(P2PDataStorage.ByteArray key) {
synchronized (map) {
return map.containsKey(key);
}
}
public P2PDataStorage.MapValue get(P2PDataStorage.ByteArray key) {
synchronized (map) {
return map.get(key);
}
}
public void put(P2PDataStorage.ByteArray key, P2PDataStorage.MapValue value) {
synchronized (map) {
map.put(key, value);
}
}
}