Repository: incubator-gossip Updated Branches: refs/heads/master 6ef0eb788 -> 95cce48a8
GOSSIP-66 Implement Crdt 2P-Set Project: http://git-wip-us.apache.org/repos/asf/incubator-gossip/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-gossip/commit/89af0ac1 Tree: http://git-wip-us.apache.org/repos/asf/incubator-gossip/tree/89af0ac1 Diff: http://git-wip-us.apache.org/repos/asf/incubator-gossip/diff/89af0ac1 Branch: refs/heads/master Commit: 89af0ac11289e7448a00382a0a93c460d9bfce5c Parents: f71460a Author: Maxim Rusak <[email protected]> Authored: Fri Jun 30 10:15:26 2017 +0300 Committer: Maxim Rusak <[email protected]> Committed: Fri Jun 30 10:16:22 2017 +0300 ---------------------------------------------------------------------- .../java/org/apache/gossip/crdt/CrdtModule.java | 8 ++ .../org/apache/gossip/crdt/TwoPhaseSet.java | 115 ++++++++++++++++ .../gossip/crdt/AbstractCRDTStringSetTest.java | 133 ------------------ .../gossip/crdt/AddRemoveStringSetTest.java | 137 +++++++++++++++++++ .../java/org/apache/gossip/crdt/LwwSetTest.java | 2 +- .../apache/gossip/crdt/MaxChangeSetTest.java | 2 +- .../java/org/apache/gossip/crdt/OrSetTest.java | 2 +- .../org/apache/gossip/crdt/TwoPhaseSetTest.java | 101 ++++++++++++++ .../test/java/org/apache/gossip/DataTest.java | 6 + .../gossip/protocol/json/JacksonTest.java | 12 +- 10 files changed, 379 insertions(+), 139 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java ---------------------------------------------------------------------- diff --git a/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java b/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java index 7ec96e7..ab5cefa 100644 --- a/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java +++ b/gossip-base/src/main/java/org/apache/gossip/crdt/CrdtModule.java @@ -54,6 +54,13 @@ abstract class MaxChangeSetMixin<E> { @JsonProperty("data") abstract Map<E, Integer> getStruct(); } +abstract class TwoPhaseSetMixin<E> { + @JsonCreator + TwoPhaseSetMixin(@JsonProperty("added") Set<E> added, @JsonProperty("removed") Set<E> removed) { } + @JsonProperty("added") abstract Set<E> getAdded(); + @JsonProperty("removed") abstract Set<E> getRemoved(); +} + abstract class GrowOnlySetMixin<E>{ @JsonCreator GrowOnlySetMixin(@JsonProperty("elements") Set<E> elements){ } @@ -93,6 +100,7 @@ public class CrdtModule extends SimpleModule { context.setMixInAnnotations(LwwSet.class, LWWSetMixin.class); context.setMixInAnnotations(LwwSet.Timestamps.class, LWWSetTimestampsMixin.class); context.setMixInAnnotations(MaxChangeSet.class, MaxChangeSetMixin.class); + context.setMixInAnnotations(TwoPhaseSet.class, TwoPhaseSetMixin.class); } } http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/main/java/org/apache/gossip/crdt/TwoPhaseSet.java ---------------------------------------------------------------------- diff --git a/gossip-base/src/main/java/org/apache/gossip/crdt/TwoPhaseSet.java b/gossip-base/src/main/java/org/apache/gossip/crdt/TwoPhaseSet.java new file mode 100644 index 0000000..a1f44a9 --- /dev/null +++ b/gossip-base/src/main/java/org/apache/gossip/crdt/TwoPhaseSet.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.gossip.crdt; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.function.BiFunction; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/* + Two-Phase CrdtSet. + You can add element only once and remove only once. + You cannot remove element which is not present. + + Read more: https://github.com/aphyr/meangirls#2p-set + You can view examples of usage in tests: + TwoPhaseSetTest - unit tests + DataTest - integration test with 2 nodes, TwoPhaseSet was serialized/deserialized, sent between nodes, merged +*/ + +public class TwoPhaseSet<ElementType> implements CrdtAddRemoveSet<ElementType, Set<ElementType>, TwoPhaseSet<ElementType>> { + private final Set<ElementType> added; + private final Set<ElementType> removed; + + public TwoPhaseSet(){ + added = new HashSet<>(); + removed = new HashSet<>(); + } + + @SafeVarargs + public TwoPhaseSet(ElementType... elements){ + this(new HashSet<>(Arrays.asList(elements))); + } + + public TwoPhaseSet(Set<ElementType> set){ + this(); + for (ElementType e : set){ + added.add(e); + } + } + + public TwoPhaseSet(TwoPhaseSet<ElementType> first, TwoPhaseSet<ElementType> second){ + BiFunction<Set<ElementType>, Set<ElementType>, Set<ElementType>> mergeSets = (f, s) -> + Stream.concat(f.stream(), s.stream()).collect(Collectors.toSet()); + + added = mergeSets.apply(first.added, second.added); + removed = mergeSets.apply(first.removed, second.removed); + } + + TwoPhaseSet(Set<ElementType> added, Set<ElementType> removed){ + this.added = added; + this.removed = removed; + } + + Set<ElementType> getAdded(){ + return added; + } + + Set<ElementType> getRemoved(){ + return removed; + } + + public TwoPhaseSet<ElementType> add(ElementType e){ + if (removed.contains(e) || added.contains(e)){ + return this; + } + return this.merge(new TwoPhaseSet<>(e)); + } + + public TwoPhaseSet<ElementType> remove(ElementType e){ + if (removed.contains(e) || !added.contains(e)){ + return this; + } + Set<ElementType> eSet = new HashSet<>(Collections.singletonList(e)); + return this.merge(new TwoPhaseSet<>(eSet, eSet)); + } + + @Override + public TwoPhaseSet<ElementType> merge(TwoPhaseSet<ElementType> other){ + return new TwoPhaseSet<>(this, other); + } + + @Override + public Set<ElementType> value(){ + return added.stream().filter(e -> !removed.contains(e)).collect(Collectors.toSet()); + } + + @Override + public TwoPhaseSet<ElementType> optimize(){ + return new TwoPhaseSet<>(value(), removed); + } + + @Override + public boolean equals(Object obj){ + return this == obj || (obj != null && getClass() == obj.getClass() && value().equals(((TwoPhaseSet) obj).value())); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/AbstractCRDTStringSetTest.java ---------------------------------------------------------------------- diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/AbstractCRDTStringSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/AbstractCRDTStringSetTest.java deleted file mode 100644 index d4db4ce..0000000 --- a/gossip-base/src/test/java/org/apache/gossip/crdt/AbstractCRDTStringSetTest.java +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unle<F4>ss required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.apache.gossip.crdt; - -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.junit.Ignore; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/* - Abstract test suit to test CrdtSets with Add and Remove operations. - It compares them with simple sets, validates add, remove, equals, value, etc. operations - To use it you should: - 1. subclass this and implement constructors - 2. implement CrdtAddRemoveSet in your CrdtSet - 3. make your CrdtSet immutable -*/ - -@Ignore -public abstract class AbstractCRDTStringSetTest<SetType extends CrdtAddRemoveSet<String, Set<String>, SetType>> { - abstract SetType construct(Set<String> set); - - abstract SetType construct(); - - private Set<String> sampleSet; - - @Before - public void setup(){ - sampleSet = new HashSet<>(); - sampleSet.add("4"); - sampleSet.add("5"); - sampleSet.add("12"); - } - - @Test - public void abstractSetConstructorTest(){ - Assert.assertEquals(construct(sampleSet).value(), sampleSet); - } - - @Test - public void abstractStressWithSetTest(){ - Set<String> hashSet = new HashSet<>(); - SetType set = construct(); - for (int it = 0; it < 40; it++){ - SetType newSet; - if (it % 5 == 1){ - //deleting existing - String forDelete = hashSet.stream().skip((long) (hashSet.size() * Math.random())).findFirst().get(); - newSet = set.remove(forDelete); - Assert.assertEquals(set.value(), hashSet); // check old version is immutable - hashSet.remove(forDelete); - } else { - //adding - String forAdd = String.valueOf((int) (10000 * Math.random())); - newSet = set.add(forAdd); - Assert.assertEquals(set.value(), hashSet); // check old version is immutable - hashSet.add(forAdd); - } - set = newSet; - Assert.assertEquals(set.value(), hashSet); - } - } - - @Test - public void abstractEqualsTest(){ - SetType set = construct(sampleSet); - Assert.assertFalse(set.equals(sampleSet)); - SetType newSet = set.add("25"); - sampleSet.add("25"); - Assert.assertFalse(newSet.equals(set)); - Assert.assertEquals(construct(sampleSet), newSet); - } - - @Test - public void abstractRemoveMissingTest(){ - SetType set = construct(sampleSet); - set = set.add("25"); - set = set.remove("25"); - Assert.assertEquals(set.value(), sampleSet); - set = set.remove("25"); - set = set.add("25"); - sampleSet.add("25"); - Assert.assertEquals(set.value(), sampleSet); - } - - @Test - public void abstractStressMergeTest(){ - // in one-process context, add, remove and merge operations of lww are equal to operations of Set - // we've already checked it. Now just check merge - Set<String> hashSet1 = new HashSet<>(), hashSet2 = new HashSet<>(); - SetType set1 = construct(), set2 = construct(); - - for (int it = 0; it < 100; it++){ - String forAdd = String.valueOf((int) (10000 * Math.random())); - if (it % 2 == 0){ - hashSet1.add(forAdd); - set1 = set1.add(forAdd); - } else { - hashSet2.add(forAdd); - set2 = set2.add(forAdd); - } - } - Assert.assertEquals(set1.value(), hashSet1); - Assert.assertEquals(set2.value(), hashSet2); - Set<String> mergedSet = Stream.concat(hashSet1.stream(), hashSet2.stream()).collect(Collectors.toSet()); - Assert.assertEquals(set1.merge(set2).value(), mergedSet); - } - - @Test - public void abstractOptimizeTest(){ - Assert.assertEquals(construct(sampleSet).value(), sampleSet); - Assert.assertEquals(construct(sampleSet).optimize().value(), sampleSet); - } -} http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/AddRemoveStringSetTest.java ---------------------------------------------------------------------- diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/AddRemoveStringSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/AddRemoveStringSetTest.java new file mode 100644 index 0000000..6dac9df --- /dev/null +++ b/gossip-base/src/test/java/org/apache/gossip/crdt/AddRemoveStringSetTest.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unle<F4>ss required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.gossip.crdt; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/* + Abstract test suit to test CrdtSets with Add and Remove operations. + You can use this suite only if your set supports multiple additions/deletions + and has behavior similar to Set in single-threaded environment. + It compares them with simple sets, validates add, remove, equals, value, etc. operations + To use it you should: + 1. subclass this and implement constructors + 2. implement CrdtAddRemoveSet in your CrdtSet + 3. make your CrdtSet immutable +*/ + +@Ignore +public abstract class AddRemoveStringSetTest<SetType extends CrdtAddRemoveSet<String, Set<String>, SetType>> { + + abstract SetType construct(Set<String> set); + + abstract SetType construct(); + + private Set<String> sampleSet; + + @Before + public void setup(){ + sampleSet = new HashSet<>(); + sampleSet.add("4"); + sampleSet.add("5"); + sampleSet.add("12"); + } + + @Test + public void abstractSetConstructorTest(){ + Assert.assertEquals(construct(sampleSet).value(), sampleSet); + } + + @Test + public void abstractStressWithSetTest(){ + Set<String> hashSet = new HashSet<>(); + SetType set = construct(); + for (int it = 0; it < 40; it++){ + SetType newSet; + if (it % 5 == 1){ + //deleting existing + String forDelete = hashSet.stream().skip((long) (hashSet.size() * Math.random())).findFirst().get(); + newSet = set.remove(forDelete); + Assert.assertEquals(set.value(), hashSet); // check old version is immutable + hashSet.remove(forDelete); + } else { + //adding + String forAdd = String.valueOf((int) (10000 * Math.random())); + newSet = set.add(forAdd); + Assert.assertEquals(set.value(), hashSet); // check old version is immutable + hashSet.add(forAdd); + } + set = newSet; + Assert.assertEquals(set.value(), hashSet); + } + } + + @Test + public void abstractEqualsTest(){ + SetType set = construct(sampleSet); + Assert.assertFalse(set.equals(sampleSet)); + SetType newSet = set.add("25"); + sampleSet.add("25"); + Assert.assertFalse(newSet.equals(set)); + Assert.assertEquals(construct(sampleSet), newSet); + } + + @Test + public void abstractRemoveMissingTest(){ + SetType set = construct(sampleSet); + set = set.add("25"); + set = set.remove("25"); + Assert.assertEquals(set.value(), sampleSet); + set = set.remove("25"); + set = set.add("25"); + sampleSet.add("25"); + Assert.assertEquals(set.value(), sampleSet); + } + + @Test + public void abstractStressMergeTest(){ + // in one-process context, add, remove and merge operations of lww are equal to operations of Set + // we've already checked it. Now just check merge + Set<String> hashSet1 = new HashSet<>(), hashSet2 = new HashSet<>(); + SetType set1 = construct(), set2 = construct(); + + for (int it = 0; it < 100; it++){ + String forAdd = String.valueOf((int) (10000 * Math.random())); + if (it % 2 == 0){ + hashSet1.add(forAdd); + set1 = set1.add(forAdd); + } else { + hashSet2.add(forAdd); + set2 = set2.add(forAdd); + } + } + Assert.assertEquals(set1.value(), hashSet1); + Assert.assertEquals(set2.value(), hashSet2); + Set<String> mergedSet = Stream.concat(hashSet1.stream(), hashSet2.stream()).collect(Collectors.toSet()); + Assert.assertEquals(set1.merge(set2).value(), mergedSet); + } + + @Test + public void abstractOptimizeTest(){ + Assert.assertEquals(construct(sampleSet).value(), sampleSet); + Assert.assertEquals(construct(sampleSet).optimize().value(), sampleSet); + } +} http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java ---------------------------------------------------------------------- diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java index 8200b15..c4da83d 100644 --- a/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java +++ b/gossip-base/src/test/java/org/apache/gossip/crdt/LwwSetTest.java @@ -27,7 +27,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -public class LwwSetTest extends AbstractCRDTStringSetTest<LwwSet<String>> { +public class LwwSetTest extends AddRemoveStringSetTest<LwwSet<String>> { static private Clock clock = new SystemClock(); LwwSet<String> construct(Set<String> set){ http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java ---------------------------------------------------------------------- diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java index 2ba3f09..3828747 100644 --- a/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java +++ b/gossip-base/src/test/java/org/apache/gossip/crdt/MaxChangeSetTest.java @@ -25,7 +25,7 @@ import java.util.HashSet; import java.util.Map; import java.util.Set; -public class MaxChangeSetTest extends AbstractCRDTStringSetTest<MaxChangeSet<String>> { +public class MaxChangeSetTest extends AddRemoveStringSetTest<MaxChangeSet<String>> { MaxChangeSet<String> construct(Set<String> set){ return new MaxChangeSet<>(set); } http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java ---------------------------------------------------------------------- diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java index bdaada9..8b21360 100644 --- a/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java +++ b/gossip-base/src/test/java/org/apache/gossip/crdt/OrSetTest.java @@ -25,7 +25,7 @@ import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; -public class OrSetTest extends AbstractCRDTStringSetTest<OrSet<String>> { +public class OrSetTest extends AddRemoveStringSetTest<OrSet<String>> { OrSet<String> construct(){ return new OrSet<>(); } http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-base/src/test/java/org/apache/gossip/crdt/TwoPhaseSetTest.java ---------------------------------------------------------------------- diff --git a/gossip-base/src/test/java/org/apache/gossip/crdt/TwoPhaseSetTest.java b/gossip-base/src/test/java/org/apache/gossip/crdt/TwoPhaseSetTest.java new file mode 100644 index 0000000..3af1920 --- /dev/null +++ b/gossip-base/src/test/java/org/apache/gossip/crdt/TwoPhaseSetTest.java @@ -0,0 +1,101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.gossip.crdt; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.BiConsumer; + +public class TwoPhaseSetTest { + + private Set<String> sampleSet; + + @Before + public void setup(){ + sampleSet = new HashSet<>(); + sampleSet.add("a"); + sampleSet.add("b"); + sampleSet.add("d"); + } + + @Test + public void setConstructorTest(){ + Assert.assertEquals(new TwoPhaseSet<>(sampleSet).value(), sampleSet); + } + + @Test + public void valueTest(){ + Set<Character> added = new HashSet<>(); + added.add('a'); + added.add('b'); + Set<Character> removed = new HashSet<>(); + removed.add('b'); + Assert.assertEquals(new TwoPhaseSet<>(added, removed), new TwoPhaseSet<>('a')); + } + + @Test + public void optimizeTest(){ + TwoPhaseSet<String> set = new TwoPhaseSet<>(sampleSet); + set = set.remove("b"); + Assert.assertEquals(set.optimize(), set); + // check that optimize in this case actually works + Assert.assertTrue(set.optimize().getAdded().size() < set.getAdded().size()); + } + + @Test + public void immutabilityTest(){ + TwoPhaseSet<String> set = new TwoPhaseSet<>(sampleSet); + TwoPhaseSet<String> newSet = set.remove("b"); + Assert.assertNotEquals(set, newSet); + Assert.assertEquals(set, new TwoPhaseSet<>(sampleSet)); + } + + @Test + public void removeMissingAddExistingLimitsTest(){ + BiConsumer<TwoPhaseSet<?>, TwoPhaseSet<?>> checkInternals = (f, s) -> { + Assert.assertEquals(s, f); + Assert.assertEquals(s.getRemoved(), f.getRemoved()); + Assert.assertEquals(s.getAdded(), f.getAdded()); + }; + TwoPhaseSet<String> set = new TwoPhaseSet<>(sampleSet); + // remove missing + checkInternals.accept(set, set.remove("e")); + // add existing + checkInternals.accept(set, set.add("a")); + // limits + TwoPhaseSet<String> newSet = set.remove("a"); // allow this remove + Assert.assertEquals(newSet.add("a"), new TwoPhaseSet<>("b", "d")); // discard this add, "a" was added and removed + } + + @Test + public void mergeTest(){ + TwoPhaseSet<String> f = new TwoPhaseSet<>(sampleSet); + TwoPhaseSet<String> s = new TwoPhaseSet<>("a", "c"); + s = s.remove("a"); + TwoPhaseSet<String> res = f.merge(s); + Assert.assertEquals(res, new TwoPhaseSet<>(f, s)); // check two-sets constructor + + // "a" was both added and deleted in second set => it's deleted in result + // "b" and "d" comes from first set and "c" comes from second + Assert.assertEquals(res, new TwoPhaseSet<>("b", "c", "d")); + } +} http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-itest/src/test/java/org/apache/gossip/DataTest.java ---------------------------------------------------------------------- diff --git a/gossip-itest/src/test/java/org/apache/gossip/DataTest.java b/gossip-itest/src/test/java/org/apache/gossip/DataTest.java index df078aa..c16174f 100644 --- a/gossip-itest/src/test/java/org/apache/gossip/DataTest.java +++ b/gossip-itest/src/test/java/org/apache/gossip/DataTest.java @@ -25,6 +25,7 @@ import org.apache.gossip.crdt.LwwSet; import org.apache.gossip.crdt.MaxChangeSet; import org.apache.gossip.crdt.OrSet; import org.apache.gossip.crdt.PNCounter; +import org.apache.gossip.crdt.TwoPhaseSet; import org.apache.gossip.manager.GossipManager; import org.apache.gossip.manager.GossipManagerBuilder; import org.apache.gossip.model.PerNodeDataMessage; @@ -148,6 +149,11 @@ public class DataTest { } @Test + public void TwoPhaseSetTest(){ + crdtSetTest("crtps", TwoPhaseSet::new); + } + + @Test public void GrowOnlyCounterTest(){ Consumer<Long> assertCountUpdated = count -> { for (GossipManager client : clients){ http://git-wip-us.apache.org/repos/asf/incubator-gossip/blob/89af0ac1/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java ---------------------------------------------------------------------- diff --git a/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java b/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java index d391fa1..2a5239c 100644 --- a/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java +++ b/gossip-protocol-jackson/src/test/java/org/apache/gossip/protocol/json/JacksonTest.java @@ -25,6 +25,7 @@ import org.apache.gossip.Member; import org.apache.gossip.crdt.LwwSet; import org.apache.gossip.crdt.MaxChangeSet; import org.apache.gossip.crdt.OrSet; +import org.apache.gossip.crdt.TwoPhaseSet; import org.apache.gossip.manager.GossipManager; import org.apache.gossip.manager.GossipManagerBuilder; import org.apache.gossip.protocol.ProtocolManager; @@ -98,17 +99,22 @@ public class JacksonTest { @Test public void jacksonOrSetTest(){ - jacksonCrdtSeDeTest(new OrSet<>("1", "2", "3"), OrSet.class); + jacksonCrdtSeDeTest(new OrSet<>("1", "2", "3").remove("2"), OrSet.class); } @Test public void jacksonLWWSetTest(){ - jacksonCrdtSeDeTest(new LwwSet<>("1", "2", "3"), LwwSet.class); + jacksonCrdtSeDeTest(new LwwSet<>("1", "2", "3").remove("2"), LwwSet.class); } @Test public void jacksonMaxChangeSetTest(){ - jacksonCrdtSeDeTest(new MaxChangeSet<>("1", "2", "3"), MaxChangeSet.class); + jacksonCrdtSeDeTest(new MaxChangeSet<>("1", "2", "3").remove("2"), MaxChangeSet.class); + } + + @Test + public void jacksonTwoPhaseSetTest(){ + jacksonCrdtSeDeTest(new TwoPhaseSet<>("1", "2", "3").remove("2"), TwoPhaseSet.class); } @Test
