From c272a44efcff7d35a6ba31ef3cd12d1eb17640a0 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Wed, 26 Jul 2023 17:48:25 +0200 Subject: Versioned.commit + Versioned.restore uses Version instead of long. When a Version is collected by gc, the store lets the state get collected by gc as well. --- .../java/tools/refinery/store/map/Version.java | 26 +++++++++ .../java/tools/refinery/store/map/Versioned.java | 18 +++++- .../tools/refinery/store/map/VersionedMap.java | 2 +- .../refinery/store/map/VersionedMapStore.java | 8 +-- .../store/map/VersionedMapStoreFactoryBuilder.java | 1 + .../VersionedMapStoreFactoryBuilderImpl.java | 23 ++++++-- .../store/map/internal/delta/MapTransaction.java | 10 ++-- .../map/internal/delta/VersionedMapDeltaImpl.java | 34 ++++++++--- .../internal/delta/VersionedMapStoreDeltaImpl.java | 41 ++++++-------- .../store/map/internal/state/ImmutableNode.java | 3 +- .../state/StateBasedVersionedMapStoreFactory.java | 11 +++- .../map/internal/state/VersionedMapStateImpl.java | 6 +- .../state/VersionedMapStoreStateConfiguration.java | 8 ++- .../internal/state/VersionedMapStoreStateImpl.java | 44 ++++++--------- .../tools/refinery/store/model/Interpretation.java | 3 +- .../java/tools/refinery/store/model/Model.java | 8 +-- .../tools/refinery/store/model/ModelListener.java | 4 +- .../tools/refinery/store/model/ModelStore.java | 8 +-- .../refinery/store/model/internal/ModelImpl.java | 66 +++++++++++----------- .../model/internal/ModelStoreBuilderImpl.java | 6 +- .../store/model/internal/ModelStoreImpl.java | 37 ++++++------ .../store/model/internal/ModelVersion.java | 39 +++++++++++++ .../model/internal/VersionedInterpretation.java | 14 ++--- .../store/map/tests/fuzz/DiffCursorFuzzTest.java | 21 +++---- .../map/tests/fuzz/MultiThreadTestRunnable.java | 16 +++--- .../store/map/tests/fuzz/RestoreFuzzTest.java | 5 +- .../store/map/tests/fuzz/SharedStoreFuzzTest.java | 5 +- .../map/tests/fuzz/utils/FuzzTestCollections.java | 13 ++++- .../store/map/tests/utils/MapTestEnvironment.java | 2 +- .../refinery/store/model/tests/ModelTest.java | 5 +- 30 files changed, 298 insertions(+), 189 deletions(-) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/Version.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Version.java b/subprojects/store/src/main/java/tools/refinery/store/map/Version.java new file mode 100644 index 00000000..fa2734e4 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/Version.java @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map; + +/** + * Interface denoting versions of {@link Versioned}. + */ +public interface Version { + /** + * Hashcode should be updated in accordance with equals. + * @return a hashcode of the object. + */ + int hashCode(); + + /** + * Equivalence of two {@link Version}. This equivalence must satisfy the following constraint (in addition to the + * constraints of {@link Object#equals(Object)}: if {@code v1} and {@code v2} are {@link Version}s, and {@code v1 + * .equals(v2)}, then {@code versioned.restore(v1)} must be {@code equals} to {@code versioned.restore(v2)}. + * @param o the other object. + * @return weather the two versions are equals. + */ + boolean equals(Object o); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java b/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java index 55720db3..da12b0a9 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java @@ -5,8 +5,20 @@ */ package tools.refinery.store.map; +/** + * Object that can save and restore its state. + */ public interface Versioned { - public long commit(); - //maybe revert()? - public void restore(long state); + /** + * Saves the state of the object. + * @return an object that marks the version of the object at the time the function was called. + */ + Version commit(); + + /** + * Restores the state of the object. + * @param state a {@link Version} object that marks the version. The state must be a {@link Version} object + * returned by a previous {@link #commit()}! + */ + void restore(Version state); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java index c8226c3e..28194b58 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java @@ -16,5 +16,5 @@ public non-sealed interface VersionedMap extends AnyVersionedMap { void putAll(Cursor cursor); - DiffCursor getDiffCursor(long state); + DiffCursor getDiffCursor(Version state); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java index b24c404c..55cf08a5 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java @@ -7,17 +7,13 @@ package tools.refinery.store.map; import tools.refinery.store.map.internal.VersionedMapStoreFactoryBuilderImpl; -import java.util.Set; - public interface VersionedMapStore { VersionedMap createMap(); - VersionedMap createMap(long state); - - Set getStates(); + VersionedMap createMap(Version state); - DiffCursor getDiffCursor(long fromState, long toState); + DiffCursor getDiffCursor(Version fromState, Version toState); static VersionedMapStoreFactoryBuilder builder() { return new VersionedMapStoreFactoryBuilderImpl<>(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java index 6b4fc2a0..0ac196f2 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java @@ -20,6 +20,7 @@ public interface VersionedMapStoreFactoryBuilder { VersionedMapStoreFactoryBuilder defaultValue(V defaultValue); VersionedMapStoreFactoryBuilder strategy(StoreStrategy strategy); + VersionedMapStoreFactoryBuilder versionFreeing(boolean enabled); VersionedMapStoreFactoryBuilder stateBasedImmutableWhenCommitting(boolean transformToImmutable); VersionedMapStoreFactoryBuilder stateBasedSharingStrategy(SharingStrategy sharingStrategy); VersionedMapStoreFactoryBuilder stateBasedHashProvider(ContinuousHashProvider hashProvider); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java index 47470236..9f419ce1 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java @@ -18,6 +18,7 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt private StoreStrategy strategy = null; private Boolean transformToImmutable = null; private SharingStrategy sharingStrategy = null; + private Boolean enableVersionFreeing = null; private ContinuousHashProvider continuousHashProvider = null; private DeltaTransactionStrategy deltaTransactionStrategy = null; @@ -64,6 +65,13 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt return this; } + @Override + public VersionedMapStoreFactoryBuilder versionFreeing(boolean enabled) { + this.enableVersionFreeing = enabled; + checkStrategy(); + return this; + } + @Override public VersionedMapStoreFactoryBuilder stateBasedImmutableWhenCommitting(boolean transformToImmutable) { this.transformToImmutable = transformToImmutable; @@ -118,6 +126,7 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt yield new StateBasedVersionedMapStoreFactory<>(defaultValue, getOrDefault(transformToImmutable,true), getOrDefault(sharingStrategy, SharingStrategy.SHARED_NODE_CACHE_IN_GROUP), + getOrDefault(enableVersionFreeing, true), continuousHashProvider); } case DELTA -> new DeltaBasedVersionedMapStoreFactory<>(defaultValue, @@ -127,13 +136,15 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt @Override public String toString() { - return "VersionedMapStoreBuilder{" + - "defaultValue=" + defaultValue + + return "VersionedMapStoreFactoryBuilderImpl{" + + "defaultSet=" + defaultSet + + ", defaultValue=" + defaultValue + ", strategy=" + strategy + - ", stateBasedImmutableWhenCommitting=" + transformToImmutable + - ", stateBasedNodeSharingStrategy=" + sharingStrategy + - ", hashProvider=" + continuousHashProvider + - ", deltaStorageStrategy=" + deltaTransactionStrategy + + ", transformToImmutable=" + transformToImmutable + + ", sharingStrategy=" + sharingStrategy + + ", enableVersionFreeing=" + enableVersionFreeing + + ", continuousHashProvider=" + continuousHashProvider + + ", deltaTransactionStrategy=" + deltaTransactionStrategy + '}'; } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java index 7f9ccd7f..6f3fa6d7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java @@ -5,17 +5,19 @@ */ package tools.refinery.store.map.internal.delta; +import tools.refinery.store.map.Version; + import java.util.Arrays; import java.util.Objects; -public record MapTransaction(MapDelta[] deltas, long version, MapTransaction parent) { +public record MapTransaction(MapDelta[] deltas, MapTransaction parent, int depth) implements Version { @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(deltas); - result = prime * result + Objects.hash(parent, version); + result = prime * result + Objects.hash(parent, depth); return result; } @@ -29,11 +31,11 @@ public record MapTransaction(MapDelta[] deltas, long version, MapTra return false; @SuppressWarnings("unchecked") MapTransaction other = (MapTransaction) obj; - return Arrays.equals(deltas, other.deltas) && Objects.equals(parent, other.parent) && version == other.version; + return depth == other.depth && Objects.equals(parent, other.parent) && Arrays.equals(deltas, other.deltas); } @Override public String toString() { - return "MapTransaction " + version + " " + Arrays.toString(deltas); + return "MapTransaction " + depth + " " + Arrays.toString(deltas); } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java index 5bb864ac..c19cc817 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java @@ -38,15 +38,15 @@ public class VersionedMapDeltaImpl implements VersionedMap { } @Override - public long commit() { + public Version commit() { MapDelta[] deltas = uncommittedStore.extractAndDeleteDeltas(); - long[] versionContainer = new long[1]; - this.previous = this.store.appendTransaction(deltas, previous, versionContainer); - return versionContainer[0]; + final MapTransaction committedTransaction = this.store.appendTransaction(deltas, previous); + this.previous = committedTransaction; + return committedTransaction; } @Override - public void restore(long state) { + public void restore(Version state) { // 1. restore uncommitted states MapDelta[] uncommitted = this.uncommittedStore.extractAndDeleteDeltas(); if (uncommitted != null) { @@ -61,7 +61,7 @@ public class VersionedMapDeltaImpl implements VersionedMap { this.forward(forward); } else { List[]> backward = new ArrayList<>(); - parent = this.store.getPath(this.previous.version(), state, backward, forward); + parent = this.store.getPath(this.previous, state, backward, forward); this.backward(backward); this.forward(forward); } @@ -75,12 +75,16 @@ public class VersionedMapDeltaImpl implements VersionedMap { } protected void backward(List[]> changes) { + //Currently, this loop statement is faster. + //noinspection ForLoopReplaceableByForEach for (int i = 0; i < changes.size(); i++) { backward(changes.get(i)); } } protected void forward(MapDelta[] changes) { + //Currently, this loop statement is faster. + //noinspection ForLoopReplaceableByForEach for (int i = 0; i < changes.length; i++) { final MapDelta change = changes[i]; K key = change.getKey(); @@ -168,7 +172,7 @@ public class VersionedMapDeltaImpl implements VersionedMap { } @Override - public DiffCursor getDiffCursor(long state) { + public DiffCursor getDiffCursor(Version state) { MapDelta[] backward = this.uncommittedStore.extractDeltas(); List[]> backwardTransactions = new ArrayList<>(); List[]> forwardTransactions = new ArrayList<>(); @@ -178,7 +182,7 @@ public class VersionedMapDeltaImpl implements VersionedMap { } if (this.previous != null) { - store.getPath(this.previous.version(), state, backwardTransactions, forwardTransactions); + store.getPath(this.previous, state, backwardTransactions, forwardTransactions); } else { store.getPath(state, forwardTransactions); } @@ -216,5 +220,19 @@ public class VersionedMapDeltaImpl implements VersionedMap { throw new IllegalStateException("null value stored in map!"); } } + MapTransaction transaction = this.previous; + while(transaction != null) { + MapTransaction parent = transaction.parent(); + if(parent != null) { + if(parent.depth() != transaction.depth()-1) { + throw new IllegalStateException("Parent depths are inconsistent!"); + } + } else { + if(transaction.depth() != 0) { + throw new IllegalArgumentException("Root depth is not 0!"); + } + } + transaction = transaction.parent(); + } } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java index 7f56ea77..ed169409 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java @@ -6,6 +6,7 @@ package tools.refinery.store.map.internal.delta; import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; @@ -18,10 +19,6 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore // Static data protected final V defaultValue; - // Dynamic data - protected final Map> states = new HashMap<>(); - protected long nextID = 0; - public VersionedMapStoreDeltaImpl(boolean summarizeChanges, V defaultValue) { this.summarizeChanges = summarizeChanges; this.defaultValue = defaultValue; @@ -33,30 +30,32 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore } @Override - public VersionedMap createMap(long state) { + public VersionedMap createMap(Version state) { VersionedMapDeltaImpl result = new VersionedMapDeltaImpl<>(this, this.summarizeChanges, this.defaultValue); result.restore(state); return result; } - public synchronized MapTransaction appendTransaction(MapDelta[] deltas, MapTransaction previous, long[] versionContainer) { - long version = nextID++; - versionContainer[0] = version; + public MapTransaction appendTransaction(MapDelta[] deltas, MapTransaction previous) { if (deltas == null) { - states.put(version, previous); return previous; } else { - MapTransaction transaction = new MapTransaction<>(deltas, version, previous); - states.put(version, transaction); - return transaction; + final int depth; + if(previous != null) { + depth = previous.depth()+1; + } else { + depth = 0; + } + return new MapTransaction<>(deltas, previous, depth); } } - private synchronized MapTransaction getState(long state) { - return states.get(state); + @SuppressWarnings("unchecked") + private MapTransaction getState(Version state) { + return (MapTransaction) state; } - public MapTransaction getPath(long to, List[]> forwardTransactions) { + public MapTransaction getPath(Version to, List[]> forwardTransactions) { final MapTransaction target = getState(to); MapTransaction toTransaction = target; while (toTransaction != null) { @@ -66,7 +65,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore return target; } - public MapTransaction getPath(long from, long to, + public MapTransaction getPath(Version from, Version to, List[]> backwardTransactions, List[]> forwardTransactions) { MapTransaction fromTransaction = getState(from); @@ -74,7 +73,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore MapTransaction toTransaction = target; while (fromTransaction != toTransaction) { - if (fromTransaction == null || (toTransaction != null && fromTransaction.version() < toTransaction.version())) { + if (fromTransaction == null || (toTransaction != null && fromTransaction.depth() < toTransaction.depth())) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } else { @@ -85,14 +84,8 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore return target; } - - @Override - public synchronized Set getStates() { - return new HashSet<>(states.keySet()); - } - @Override - public DiffCursor getDiffCursor(long fromState, long toState) { + public DiffCursor getDiffCursor(Version fromState, Version toState) { List[]> backwardTransactions = new ArrayList<>(); List[]> forwardTransactions = new ArrayList<>(); getPath(fromState, toState, backwardTransactions, forwardTransactions); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java index 722f9ed7..5b1d8b77 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java @@ -9,8 +9,9 @@ import java.util.Arrays; import java.util.Map; import tools.refinery.store.map.ContinuousHashProvider; +import tools.refinery.store.map.Version; -public class ImmutableNode extends Node { +public class ImmutableNode extends Node implements Version { /** * Bitmap defining the stored key and values. */ diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java index 9409ace0..ccc791a8 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java @@ -14,14 +14,19 @@ public class StateBasedVersionedMapStoreFactory implements VersionedMapSto private final ContinuousHashProvider continuousHashProvider; private final VersionedMapStoreStateConfiguration config; - public StateBasedVersionedMapStoreFactory(V defaultValue, Boolean transformToImmutable, VersionedMapStoreFactoryBuilder.SharingStrategy sharingStrategy, ContinuousHashProvider continuousHashProvider) { + public StateBasedVersionedMapStoreFactory(V defaultValue, Boolean transformToImmutable, + VersionedMapStoreFactoryBuilder.SharingStrategy sharingStrategy, + boolean versionFreeingEnabled, + ContinuousHashProvider continuousHashProvider) { this.defaultValue = defaultValue; this.continuousHashProvider = continuousHashProvider; this.config = new VersionedMapStoreStateConfiguration( transformToImmutable, - sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE || sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, - sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP); + sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE + || sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, + sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, + versionFreeingEnabled); } @Override diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java index 817fc70b..57eeccf6 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java @@ -115,7 +115,7 @@ public class VersionedMapStateImpl implements VersionedMap { } @Override - public DiffCursor getDiffCursor(long toVersion) { + public DiffCursor getDiffCursor(Version toVersion) { InOrderMapCursor fromCursor = new InOrderMapCursor<>(this); VersionedMapStateImpl toMap = (VersionedMapStateImpl) this.store.createMap(toVersion); InOrderMapCursor toCursor = new InOrderMapCursor<>(toMap); @@ -124,7 +124,7 @@ public class VersionedMapStateImpl implements VersionedMap { @Override - public long commit() { + public Version commit() { return this.store.commit(root, this); } @@ -133,7 +133,7 @@ public class VersionedMapStateImpl implements VersionedMap { } @Override - public void restore(long state) { + public void restore(Version state) { root = this.store.revert(state); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java index 45f30cc7..6650f565 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java @@ -14,11 +14,12 @@ public class VersionedMapStoreStateConfiguration { } public VersionedMapStoreStateConfiguration(boolean immutableWhenCommitting, boolean sharedNodeCacheInStore, - boolean sharedNodeCacheInStoreGroups) { + boolean sharedNodeCacheInStoreGroups, boolean versionFreeingEnabled) { super(); this.immutableWhenCommitting = immutableWhenCommitting; this.sharedNodeCacheInStore = sharedNodeCacheInStore; this.sharedNodeCacheInStoreGroups = sharedNodeCacheInStoreGroups; + this.versionFreeingEnabled = versionFreeingEnabled; } /** @@ -53,4 +54,9 @@ public class VersionedMapStoreStateConfiguration { public boolean isSharedNodeCacheInStoreGroups() { return sharedNodeCacheInStoreGroups; } + + private boolean versionFreeingEnabled = true; + public boolean isVersionFreeingEnabled() { + return versionFreeingEnabled; + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java index 0651772a..8ff3f8e7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java @@ -17,10 +17,7 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore protected final ContinuousHashProvider hashProvider; protected final V defaultValue; - // Dynamic data - protected final Map> states = new HashMap<>(); protected final Map, ImmutableNode> nodeCache; - protected long nextID = 0; public VersionedMapStoreStateImpl(ContinuousHashProvider hashProvider, V defaultValue, VersionedMapStoreStateConfiguration config) { @@ -28,7 +25,7 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore this.hashProvider = hashProvider; this.defaultValue = defaultValue; if (config.isSharedNodeCacheInStore()) { - nodeCache = new HashMap<>(); + nodeCache = createNoteCache(config); } else { nodeCache = null; } @@ -53,7 +50,7 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore if (config.isSharedNodeCacheInStoreGroups()) { Map, ImmutableNode> nodeCache; if (config.isSharedNodeCacheInStore()) { - nodeCache = new HashMap<>(); + nodeCache = createNoteCache(config); } else { nodeCache = null; } @@ -68,39 +65,36 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore return result; } + private static Map createNoteCache(VersionedMapStoreStateConfiguration config) { + if(config.isVersionFreeingEnabled()) { + return new WeakHashMap<>(); + } else { + return new HashMap<>(); + } + } + public static List> createSharedVersionedMapStores(int amount, ContinuousHashProvider hashProvider, V defaultValue) { return createSharedVersionedMapStores(amount, hashProvider, defaultValue, new VersionedMapStoreStateConfiguration()); } - @Override - public synchronized Set getStates() { - return new HashSet<>(states.keySet()); - } - @Override public VersionedMap createMap() { return new VersionedMapStateImpl<>(this, hashProvider, defaultValue); } @Override - public VersionedMap createMap(long state) { + public VersionedMap createMap(Version state) { ImmutableNode data = revert(state); return new VersionedMapStateImpl<>(this, hashProvider, defaultValue, data); } - public synchronized ImmutableNode revert(long state) { - if (states.containsKey(state)) { - return states.get(state); - } else { - ArrayList existingKeys = new ArrayList<>(states.keySet()); - Collections.sort(existingKeys); - throw new IllegalArgumentException("Store does not contain state " + state + "! Available states: " - + Arrays.toString(existingKeys.toArray())); - } + @SuppressWarnings("unchecked") + public synchronized ImmutableNode revert(Version state) { + return (ImmutableNode) state; } - public synchronized long commit(Node data, VersionedMapStateImpl mapToUpdateRoot) { + public synchronized Version commit(Node data, VersionedMapStateImpl mapToUpdateRoot) { ImmutableNode immutable; if (data != null) { immutable = data.toImmutable(this.nodeCache); @@ -108,18 +102,14 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore immutable = null; } - if (nextID == Long.MAX_VALUE) - throw new IllegalStateException("Map store run out of Id-s"); - long id = nextID++; - this.states.put(id, immutable); if (this.immutableWhenCommitting) { mapToUpdateRoot.setRoot(immutable); } - return id; + return immutable; } @Override - public DiffCursor getDiffCursor(long fromState, long toState) { + public DiffCursor getDiffCursor(Version fromState, Version toState) { VersionedMapStateImpl map1 = (VersionedMapStateImpl) createMap(fromState); VersionedMapStateImpl map2 = (VersionedMapStateImpl) createMap(toState); InOrderMapCursor cursor1 = new InOrderMapCursor<>(map1); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java index 26ad9a69..72f188d3 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/Interpretation.java @@ -7,6 +7,7 @@ package tools.refinery.store.model; import tools.refinery.store.map.Cursor; import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.Version; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; @@ -22,7 +23,7 @@ public non-sealed interface Interpretation extends AnyInterpretation { void putAll(Cursor cursor); - DiffCursor getDiffCursor(long to); + DiffCursor getDiffCursor(Version to); void addListener(InterpretationListener listener, boolean alsoWhenRestoring); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java index d58d91c3..a028b81b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/Model.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/Model.java @@ -6,6 +6,7 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.map.Version; import tools.refinery.store.map.Versioned; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; @@ -13,11 +14,10 @@ import tools.refinery.store.representation.Symbol; import java.util.Optional; public interface Model extends Versioned { - long NO_STATE_ID = -1; - + Version NO_STATE_ID = null; ModelStore getStore(); - long getState(); + Version getState(); boolean hasUncommittedChanges(); @@ -27,7 +27,7 @@ public interface Model extends Versioned { Interpretation getInterpretation(Symbol symbol); - ModelDiffCursor getDiffCursor(long to); + ModelDiffCursor getDiffCursor(Version to); Optional tryGetAdapter(Class adapterType); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelListener.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelListener.java index a9ad8cfd..703ee10a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelListener.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelListener.java @@ -5,6 +5,8 @@ */ package tools.refinery.store.model; +import tools.refinery.store.map.Version; + public interface ModelListener { default void beforeCommit() { } @@ -12,7 +14,7 @@ public interface ModelListener { default void afterCommit() { } - default void beforeRestore(long state) { + default void beforeRestore(Version state) { } default void afterRestore() { diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java index b10eb8a4..89382b3a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelStore.java @@ -6,23 +6,21 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.map.Version; import tools.refinery.store.model.internal.ModelStoreBuilderImpl; import tools.refinery.store.representation.AnySymbol; import java.util.Collection; import java.util.Optional; -import java.util.Set; public interface ModelStore { Collection getSymbols(); Model createEmptyModel(); - Model createModelForState(long state); + Model createModelForState(Version state); - Set getStates(); - - ModelDiffCursor getDiffCursor(long from, long to); + ModelDiffCursor getDiffCursor(Version from, Version to); Optional tryGetAdapter(Class adapterType); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java index c5475a1a..c2ad9257 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelImpl.java @@ -8,6 +8,7 @@ package tools.refinery.store.model.internal; import tools.refinery.store.adapter.AdapterUtils; import tools.refinery.store.adapter.ModelAdapter; import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.Version; import tools.refinery.store.model.*; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; @@ -17,21 +18,21 @@ import java.util.*; public class ModelImpl implements Model { private final ModelStore store; - private long state; - private Map> interpretations; + private Version state; + private LinkedHashMap> interpretations; private final List adapters; private final List listeners = new ArrayList<>(); private boolean uncommittedChanges; private ModelAction pendingAction = ModelAction.NONE; - private long restoringToState = NO_STATE_ID; + private Version restoringToState = null; - ModelImpl(ModelStore store, long state, int adapterCount) { + ModelImpl(ModelStore store, Version state, int adapterCount) { this.store = store; this.state = state; adapters = new ArrayList<>(adapterCount); } - void setInterpretations(Map> interpretations) { + void setInterpretations(LinkedHashMap> interpretations) { this.interpretations = interpretations; } @@ -41,7 +42,7 @@ public class ModelImpl implements Model { } @Override - public long getState() { + public Version getState() { return state; } @@ -57,7 +58,7 @@ public class ModelImpl implements Model { } @Override - public ModelDiffCursor getDiffCursor(long to) { + public ModelDiffCursor getDiffCursor(Version to) { var diffCursors = new HashMap>(interpretations.size()); for (var entry : interpretations.entrySet()) { diffCursors.put(entry.getKey(), entry.getValue().getDiffCursor(to)); @@ -65,7 +66,7 @@ public class ModelImpl implements Model { return new ModelDiffCursor(diffCursors); } - private void setState(long state) { + private void setState(Version state) { this.state = state; uncommittedChanges = false; } @@ -82,11 +83,11 @@ public class ModelImpl implements Model { } private boolean hasPendingAction() { - return pendingAction != ModelAction.NONE || restoringToState != NO_STATE_ID; + return pendingAction != ModelAction.NONE || restoringToState != null; } @Override - public long commit() { + public Version commit() { if (hasPendingAction()) { throw pendingActionError("commit"); } @@ -94,43 +95,40 @@ public class ModelImpl implements Model { try { int listenerCount = listeners.size(); int i = listenerCount; - long version = 0; + + // Before commit message to listeners while (i > 0) { i--; listeners.get(i).beforeCommit(); } - boolean versionSet = false; - for (var interpretation : interpretations.values()) { - long newVersion = interpretation.commit(); - if (versionSet) { - if (version != newVersion) { - throw new IllegalStateException("Interpretations in model have different versions (%d and %d)" - .formatted(version, newVersion)); - } - } else { - version = newVersion; - versionSet = true; - } + + // Doing the commit on the interpretations + Version[] interpretationVersions = new Version[interpretations.size()]; + int j = 0; + for(var interpretationEntry : interpretations.entrySet()) { + interpretationVersions[j++] = interpretationEntry.getValue().commit(); } - setState(version); + ModelVersion modelVersion = new ModelVersion(interpretationVersions); + setState(modelVersion); + + // After commit message to listeners while (i < listenerCount) { listeners.get(i).afterCommit(); i++; } - return version; + + return modelVersion; } finally { pendingAction = ModelAction.NONE; } } @Override - public void restore(long version) { + public void restore(Version version) { if (hasPendingAction()) { - throw pendingActionError("restore to %d".formatted(version)); - } - if (!store.getStates().contains(version)) { - throw new IllegalArgumentException("Store does not contain state %d".formatted(version)); + throw pendingActionError("restore to %s".formatted(version)); } + pendingAction = ModelAction.RESTORE; restoringToState = version; try { @@ -140,9 +138,11 @@ public class ModelImpl implements Model { i--; listeners.get(i).beforeRestore(version); } + int j = 0; for (var interpretation : interpretations.values()) { - interpretation.restore(version); + interpretation.restore(ModelVersion.getInternalVersion(version,j++)); } + setState(version); while (i < listenerCount) { listeners.get(i).afterRestore(); @@ -150,7 +150,7 @@ public class ModelImpl implements Model { } } finally { pendingAction = ModelAction.NONE; - restoringToState = NO_STATE_ID; + restoringToState = null; } } @@ -159,7 +159,7 @@ public class ModelImpl implements Model { case NONE -> throw new IllegalArgumentException("Trying to throw pending action error when there is no " + "pending action"); case COMMIT -> "commit"; - case RESTORE -> "restore to %d".formatted(restoringToState); + case RESTORE -> "restore to %s".formatted(restoringToState); }; return new IllegalStateException("Cannot %s due to pending %s".formatted(currentActionName, pendingActionName)); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java index 2cc7b19c..65fa8d24 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreBuilderImpl.java @@ -19,8 +19,8 @@ import tools.refinery.store.tuple.Tuple; import java.util.*; public class ModelStoreBuilderImpl implements ModelStoreBuilder { - private final Set allSymbols = new HashSet<>(); - private final Map, List> equivalenceClasses = new HashMap<>(); + private final LinkedHashSet allSymbols = new LinkedHashSet<>(); + private final LinkedHashMap, List> equivalenceClasses = new LinkedHashMap<>(); private final List adapters = new ArrayList<>(); @Override @@ -59,7 +59,7 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { @Override public ModelStore build() { - var stores = new HashMap>(allSymbols.size()); + var stores = new LinkedHashMap>(allSymbols.size()); for (var entry : equivalenceClasses.entrySet()) { createStores(stores, entry.getKey(), entry.getValue()); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java index 60b735e6..a320a618 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelStoreImpl.java @@ -8,8 +8,8 @@ package tools.refinery.store.model.internal; import tools.refinery.store.adapter.AdapterUtils; import tools.refinery.store.adapter.ModelStoreAdapter; import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelDiffCursor; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.AnySymbol; @@ -18,10 +18,10 @@ import tools.refinery.store.tuple.Tuple; import java.util.*; public class ModelStoreImpl implements ModelStore { - private final Map> stores; + private final LinkedHashMap> stores; private final List adapters; - ModelStoreImpl(Map> stores, int adapterCount) { + ModelStoreImpl(LinkedHashMap> stores, int adapterCount) { this.stores = stores; adapters = new ArrayList<>(adapterCount); } @@ -31,14 +31,14 @@ public class ModelStoreImpl implements ModelStore { return Collections.unmodifiableCollection(stores.keySet()); } - private ModelImpl createModelWithoutInterpretations(long state) { + private ModelImpl createModelWithoutInterpretations(Version state) { return new ModelImpl(this, state, adapters.size()); } @Override public ModelImpl createEmptyModel() { - var model = createModelWithoutInterpretations(Model.NO_STATE_ID); - var interpretations = new HashMap>(stores.size()); + var model = createModelWithoutInterpretations(null); + var interpretations = new LinkedHashMap>(stores.size()); for (var entry : this.stores.entrySet()) { var symbol = entry.getKey(); interpretations.put(symbol, VersionedInterpretation.of(model, symbol, entry.getValue())); @@ -49,13 +49,21 @@ public class ModelStoreImpl implements ModelStore { } @Override - public synchronized ModelImpl createModelForState(long state) { + public synchronized ModelImpl createModelForState(Version state) { var model = createModelWithoutInterpretations(state); - var interpretations = new HashMap>(stores.size()); + var interpretations = new LinkedHashMap>(stores.size()); + + int i=0; for (var entry : this.stores.entrySet()) { var symbol = entry.getKey(); - interpretations.put(symbol, VersionedInterpretation.of(model, symbol, entry.getValue(), state)); + interpretations.put(symbol, + VersionedInterpretation.of( + model, + symbol, + entry.getValue(), + ModelVersion.getInternalVersion(state,i++))); } + model.setInterpretations(interpretations); adaptModel(model); return model; @@ -69,16 +77,7 @@ public class ModelStoreImpl implements ModelStore { } @Override - public synchronized Set getStates() { - var iterator = stores.values().iterator(); - if (iterator.hasNext()) { - return Set.copyOf(iterator.next().getStates()); - } - return Set.of(0L); - } - - @Override - public synchronized ModelDiffCursor getDiffCursor(long from, long to) { + public synchronized ModelDiffCursor getDiffCursor(Version from, Version to) { var diffCursors = new HashMap>(); for (var entry : stores.entrySet()) { var representation = entry.getKey(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java new file mode 100644 index 00000000..cf3b7fc6 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.model.internal; + +import tools.refinery.store.map.Version; + +import java.util.Arrays; + +public record ModelVersion(Version[] mapVersions) implements Version{ + + public static Version getInternalVersion(Version modelVersion, int interpretationIndex) { + return ((ModelVersion)modelVersion).mapVersions()[interpretationIndex]; + } + + @Override + public int hashCode() { + return Arrays.hashCode(mapVersions); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ModelVersion that = (ModelVersion) o; + + return Arrays.equals(mapVersions, that.mapVersions); + } + + @Override + public String toString() { + return "ModelVersion{" + + "mapVersions=" + Arrays.toString(mapVersions) + + '}'; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java index 404be65f..76e3baea 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/VersionedInterpretation.java @@ -5,10 +5,7 @@ */ package tools.refinery.store.model.internal; -import tools.refinery.store.map.Cursor; -import tools.refinery.store.map.DiffCursor; -import tools.refinery.store.map.VersionedMap; -import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.*; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.InterpretationListener; import tools.refinery.store.model.Model; @@ -110,15 +107,14 @@ public class VersionedInterpretation implements Interpretation { } @Override - public DiffCursor getDiffCursor(long to) { + public DiffCursor getDiffCursor(Version to) { return map.getDiffCursor(to); } - public long commit() { + Version commit() { return map.commit(); } - - public void restore(long state) { + void restore(Version state) { if (!restoreListeners.isEmpty()) { var diffCursor = getDiffCursor(state); while (diffCursor.move()) { @@ -150,7 +146,7 @@ public class VersionedInterpretation implements Interpretation { } static VersionedInterpretation of(ModelImpl model, AnySymbol symbol, VersionedMapStore store, - long state) { + Version state) { @SuppressWarnings("unchecked") var typedSymbol = (Symbol) symbol; var map = store.createMap(state); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index 5a4f8038..94259edc 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -10,13 +10,12 @@ import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.DiffCursor; -import tools.refinery.store.map.VersionedMap; -import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; +import tools.refinery.store.map.*; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; +import java.util.HashMap; +import java.util.Map; import java.util.Random; import java.util.stream.Stream; @@ -39,6 +38,7 @@ class DiffCursorFuzzTest { int commitFrequency, boolean commitBeforeDiffCursor) { int largestCommit = -1; + Map index2Version = new HashMap<>(); { // 1. build a map with versions @@ -55,8 +55,9 @@ class DiffCursorFuzzTest { fail(scenario + ":" + index + ": exception happened: " + exception); } if (index % commitFrequency == 0) { - long version = versioned.commit(); - largestCommit = (int) version; + Version version = versioned.commit(); + index2Version.put(index,version); + largestCommit = index; } if (index % 10000 == 0) System.out.println(scenario + ":" + index + "/" + steps + " building finished"); @@ -73,20 +74,20 @@ class DiffCursorFuzzTest { int index = i + 1; if (index % diffTravelFrequency == 0) { // diff-travel - long travelToVersion = r2.nextInt(largestCommit + 1); + int travelToVersion = r2.nextInt(largestCommit + 1); - VersionedMap oracle = store.createMap(travelToVersion); + VersionedMap oracle = store.createMap(index2Version.get(travelToVersion)); if(commitBeforeDiffCursor) { moving.commit(); } - DiffCursor diffCursor = moving.getDiffCursor(travelToVersion); + DiffCursor diffCursor = moving.getDiffCursor(index2Version.get(travelToVersion)); moving.putAll(diffCursor); moving.commit(); MapTestEnvironment.compareTwoMaps(scenario + ":c" + index, oracle, moving); - moving.restore(travelToVersion); + moving.restore(index2Version.get(travelToVersion)); } else { // random puts diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java index 9b2e591a..dfe46bae 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Random; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -61,7 +62,7 @@ public class MultiThreadTestRunnable implements Runnable { // 1. build a map with versions Random r = new Random(seed); VersionedMap versioned = store.createMap(); - Map index2Version = new HashMap<>(); + Map index2Version = new HashMap<>(); for (int i = 0; i < steps; i++) { int index = i + 1; @@ -74,7 +75,7 @@ public class MultiThreadTestRunnable implements Runnable { logAndThrowError(scenario + ":" + index + ": exception happened: " + exception); } if (index % commitFrequency == 0) { - long version = versioned.commit(); + Version version = versioned.commit(); index2Version.put(i, version); } MapTestEnvironment.printStatus(scenario, index, steps, "building"); @@ -100,13 +101,12 @@ public class MultiThreadTestRunnable implements Runnable { MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned, null); // go back to a random state (probably created by another thread) - List states = new ArrayList<>(store.getStates()); - states.sort(Long::compare); + List states = new ArrayList<>(index2Version.values()); + //states.sort(Long::compare); Collections.shuffle(states, r2); - for (Long state : states.subList(0, Math.min(states.size(), 100))) { - long x = state; - versioned.restore(x); - var clean = store.createMap(x); + for (Version state : states.subList(0, Math.min(states.size(), 100))) { + versioned.restore(state); + var clean = store.createMap(state); MapTestEnvironment.compareTwoMaps(scenario + ":" + index, clean, versioned, null); } versioned.restore(index2Version.get(i)); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java index 0b399c3a..5c768788 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; @@ -40,7 +41,7 @@ class RestoreFuzzTest { // 1. build a map with versions Random r = new Random(seed); VersionedMap versioned = store.createMap(); - Map index2Version = new HashMap<>(); + Map index2Version = new HashMap<>(); for (int i = 0; i < steps; i++) { int index = i + 1; @@ -53,7 +54,7 @@ class RestoreFuzzTest { fail(scenario + ":" + index + ": exception happened: " + exception); } if (index % commitFrequency == 0) { - long version = versioned.commit(); + Version version = versioned.commit(); index2Version.put(i, version); } MapTestEnvironment.printStatus(scenario, index, steps, "building"); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java index b17766b7..299c94b1 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import tools.refinery.store.map.ContinuousHashProvider; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMapStore; import tools.refinery.store.map.internal.state.VersionedMapStoreStateImpl; import tools.refinery.store.map.internal.state.VersionedMapStateImpl; @@ -47,7 +48,7 @@ class SharedStoreFuzzTest { versioneds.add((VersionedMapStateImpl) store.createMap()); } - List> index2Version = new LinkedList<>(); + List> index2Version = new LinkedList<>(); for (int i = 0; i < stores.size(); i++) { index2Version.add(new HashMap<>()); } @@ -59,7 +60,7 @@ class SharedStoreFuzzTest { String nextValue = values[r.nextInt(values.length)]; versioneds.get(storeIndex).put(nextKey, nextValue); if (stepIndex % commitFrequency == 0) { - long version = versioneds.get(storeIndex).commit(); + Version version = versioneds.get(storeIndex).commit(); index2Version.get(storeIndex).put(i, version); } MapTestEnvironment.printStatus(scenario, stepIndex, steps, "building"); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java index 4c3ecb09..ec04904e 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java @@ -18,26 +18,35 @@ public final class FuzzTestCollections { public static final Object[] randomSeedOptions = {1}; public static final Object[] storeConfigs = { // State based + // Default VersionedMapStore.builder() - .stateBasedImmutableWhenCommitting(true) .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + // Evil hash code test VersionedMapStore.builder() - .stateBasedImmutableWhenCommitting(true) .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(true)) .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + // No weak hashmap test + VersionedMapStore.builder() + .versionFreeing(false) + .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + // Copy when committing, do not hurt the work copy, share between saves. VersionedMapStore.builder() .stateBasedImmutableWhenCommitting(false) .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + // Copy when committing, do not hurt the work copy, do not share between states. VersionedMapStore.builder() .stateBasedImmutableWhenCommitting(false) .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.NO_NODE_CACHE), // Delta based + // Set based transactions VersionedMapStore.builder() .deltaTransactionStrategy(VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.SET), + // List based transactions VersionedMapStore.builder() .deltaTransactionStrategy(VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.LIST) }; diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java index 401f2866..b84df280 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java @@ -125,7 +125,7 @@ public class MapTestEnvironment { } } - public long commit(){ + public Version commit(){ return sut.commit(); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java index 56b75804..dc7b776e 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java @@ -6,6 +6,7 @@ package tools.refinery.store.model.tests; import org.junit.jupiter.api.Test; +import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.Symbol; @@ -120,7 +121,7 @@ class ModelTest { assertTrue(model.hasUncommittedChanges()); assertEquals(Model.NO_STATE_ID, model.getState()); - long state1 = model.commit(); + Version state1 = model.commit(); assertFalse(model.hasUncommittedChanges()); assertEquals(state1, model.getState()); @@ -134,7 +135,7 @@ class ModelTest { assertTrue(model.hasUncommittedChanges()); assertEquals(state1, model.getState()); - long state2 = model.commit(); + Version state2 = model.commit(); assertFalse(model.hasUncommittedChanges()); assertEquals(state2, model.getState()); -- cgit v1.2.3-54-g00ecf