GOSSIP-62 Implement Crdt PN-Counter
restored layout of pom.xml for minimal changes in PR Snapshot - I think I have the basic framework in place. No tests are passing and nothing works, but most of the calls and the builder are in place. capture working code starting working on example code Working examples GOSSIP-65 Implement crdt LWW-Element-Set LWWSet implemented + se/de + unit tests + jackson tests + DataTests GOSSIP-55 Added event handlers to notify share data and per node data changes Reformat code to match apache standard Fixed DataTest errors WRT PNCounter
This commit is contained in:
@ -61,6 +61,13 @@ abstract class GrowOnlyCounterMixin {
|
||||
@JsonProperty("counters") abstract Map<String, Long> getCounters();
|
||||
}
|
||||
|
||||
abstract class PNCounterMixin {
|
||||
@JsonCreator
|
||||
PNCounterMixin(@JsonProperty("p-counters") Map<String, Long> up, @JsonProperty("n-counters") Map<String,Long> down) { }
|
||||
@JsonProperty("p-counters") abstract Map<String, Long> getPCounters();
|
||||
@JsonProperty("n-counters") abstract Map<String, Long> getNCounters();
|
||||
}
|
||||
|
||||
//If anyone wants to take a stab at this. please have at it
|
||||
//https://github.com/FasterXML/jackson-datatype-guava/blob/master/src/main/java/com/fasterxml/jackson/datatype/guava/ser/MultimapSerializer.java
|
||||
public class CrdtModule extends SimpleModule {
|
||||
@ -76,6 +83,7 @@ public class CrdtModule extends SimpleModule {
|
||||
context.setMixInAnnotations(OrSet.class, OrSetMixin.class);
|
||||
context.setMixInAnnotations(GrowOnlySet.class, GrowOnlySetMixin.class);
|
||||
context.setMixInAnnotations(GrowOnlyCounter.class, GrowOnlyCounterMixin.class);
|
||||
context.setMixInAnnotations(PNCounter.class, PNCounterMixin.class);
|
||||
context.setMixInAnnotations(LWWSet.class, LWWSetMixin.class);
|
||||
context.setMixInAnnotations(LWWSet.Timestamps.class, LWWSetTimestampsMixin.class);
|
||||
}
|
||||
|
139
gossip-base/src/main/java/org/apache/gossip/crdt/PNCounter.java
Normal file
139
gossip-base/src/main/java/org/apache/gossip/crdt/PNCounter.java
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.Map;
|
||||
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
|
||||
public class PNCounter implements CrdtCounter<Long, PNCounter> {
|
||||
|
||||
private final GrowOnlyCounter pCount;
|
||||
|
||||
private final GrowOnlyCounter nCount;
|
||||
|
||||
PNCounter(Map<String, Long> pCounters, Map<String, Long> nCounters) {
|
||||
pCount = new GrowOnlyCounter(pCounters);
|
||||
nCount = new GrowOnlyCounter(nCounters);
|
||||
}
|
||||
|
||||
public PNCounter(PNCounter starter, Builder builder) {
|
||||
GrowOnlyCounter.Builder pBuilder = builder.makeGrowOnlyCounterBuilder(builder.pCount());
|
||||
pCount = new GrowOnlyCounter(starter.pCount, pBuilder);
|
||||
GrowOnlyCounter.Builder nBuilder = builder.makeGrowOnlyCounterBuilder(builder.nCount());
|
||||
nCount = new GrowOnlyCounter(starter.nCount, nBuilder);
|
||||
}
|
||||
|
||||
public PNCounter(Builder builder) {
|
||||
GrowOnlyCounter.Builder pBuilder = builder.makeGrowOnlyCounterBuilder(builder.pCount());
|
||||
pCount = new GrowOnlyCounter(pBuilder);
|
||||
GrowOnlyCounter.Builder nBuilder = builder.makeGrowOnlyCounterBuilder(builder.nCount());
|
||||
nCount = new GrowOnlyCounter(nBuilder);
|
||||
}
|
||||
|
||||
public PNCounter(GossipManager manager) {
|
||||
pCount = new GrowOnlyCounter(manager);
|
||||
nCount = new GrowOnlyCounter(manager);
|
||||
}
|
||||
|
||||
public PNCounter(PNCounter starter, PNCounter other) {
|
||||
pCount = new GrowOnlyCounter(starter.pCount, other.pCount);
|
||||
nCount = new GrowOnlyCounter(starter.nCount, other.nCount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PNCounter merge(PNCounter other) {
|
||||
return new PNCounter(this, other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long value() {
|
||||
long pValue = (long) pCount.value();
|
||||
long nValue = (long) nCount.value();
|
||||
return pValue - nValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PNCounter optimize() {
|
||||
return new PNCounter(pCount.getCounters(), nCount.getCounters());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
PNCounter other = (PNCounter) obj;
|
||||
return value().longValue() == other.value().longValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PnCounter [pCount=" + pCount + ", nCount=" + nCount + ", value=" + value() + "]";
|
||||
}
|
||||
|
||||
Map<String, Long> getPCounters() {
|
||||
return pCount.getCounters();
|
||||
}
|
||||
|
||||
Map<String, Long> getNCounters() {
|
||||
return nCount.getCounters();
|
||||
}
|
||||
|
||||
public static class Builder {
|
||||
|
||||
private final GossipManager myManager;
|
||||
|
||||
private long value = 0L;
|
||||
|
||||
public Builder(GossipManager gossipManager) {
|
||||
myManager = gossipManager;
|
||||
}
|
||||
|
||||
public long pCount() {
|
||||
if (value > 0) {
|
||||
return value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public long nCount() {
|
||||
if (value < 0) {
|
||||
return -value;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public org.apache.gossip.crdt.GrowOnlyCounter.Builder makeGrowOnlyCounterBuilder(long value) {
|
||||
org.apache.gossip.crdt.GrowOnlyCounter.Builder ret = new org.apache.gossip.crdt.GrowOnlyCounter.Builder(
|
||||
myManager);
|
||||
ret.increment(value);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public PNCounter.Builder increment(long delta) {
|
||||
value += delta;
|
||||
return this;
|
||||
}
|
||||
|
||||
public PNCounter.Builder decrement(long delta) {
|
||||
value -= delta;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
*
|
||||
* 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 static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.gossip.LocalMember;
|
||||
import org.apache.gossip.manager.GossipManager;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PNCounterTest {
|
||||
|
||||
private List<GossipManager> mockManagers;
|
||||
|
||||
@Before
|
||||
public void setupMocks() {
|
||||
GossipManager manager1 = mock(GossipManager.class);
|
||||
LocalMember mockMember1 = mock(LocalMember.class);
|
||||
when(mockMember1.getId()).thenReturn("x");
|
||||
when(manager1.getMyself()).thenReturn(mockMember1);
|
||||
|
||||
GossipManager manager2 = mock(GossipManager.class);
|
||||
LocalMember mockMember2 = mock(LocalMember.class);
|
||||
when(mockMember2.getId()).thenReturn("y");
|
||||
when(manager2.getMyself()).thenReturn(mockMember2);
|
||||
|
||||
GossipManager manager3 = mock(GossipManager.class);
|
||||
LocalMember mockMember3 = mock(LocalMember.class);
|
||||
when(mockMember3.getId()).thenReturn("z");
|
||||
when(manager3.getMyself()).thenReturn(mockMember3);
|
||||
|
||||
mockManagers = new ArrayList<GossipManager>();
|
||||
mockManagers.add(manager1);
|
||||
mockManagers.add(manager2);
|
||||
mockManagers.add(manager3);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void existanceTest() {
|
||||
PNCounter counter = new PNCounter(mockManagers.get(0));
|
||||
Assert.assertEquals(0, (long) counter.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void localOperationTest() {
|
||||
PNCounter counter = new PNCounter(mockManagers.get(0));
|
||||
Assert.assertEquals(0, (long) counter.value());
|
||||
|
||||
counter = new PNCounter(counter, new PNCounter.Builder(mockManagers.get(0)).increment(5L));
|
||||
Assert.assertEquals(5, (long) counter.value());
|
||||
|
||||
counter = new PNCounter(counter, new PNCounter.Builder(mockManagers.get(0)).increment(4L));
|
||||
Assert.assertEquals(9, (long) counter.value());
|
||||
|
||||
counter = new PNCounter(counter, new PNCounter.Builder(mockManagers.get(0)).decrement(3L));
|
||||
Assert.assertEquals(6, (long) counter.value());
|
||||
|
||||
counter = new PNCounter(counter, new PNCounter.Builder(mockManagers.get(0)).decrement(12L));
|
||||
Assert.assertEquals(-6, (long) counter.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oddballLocalOperationTest() {
|
||||
PNCounter counter = new PNCounter(mockManagers.get(0));
|
||||
Assert.assertEquals(0, (long) counter.value());
|
||||
|
||||
counter = new PNCounter(counter, new PNCounter.Builder(mockManagers.get(0)).increment(-5L));
|
||||
Assert.assertEquals(-5, (long) counter.value());
|
||||
|
||||
counter = new PNCounter(counter, new PNCounter.Builder(mockManagers.get(0)).increment(4L));
|
||||
Assert.assertEquals(-1, (long) counter.value());
|
||||
|
||||
counter = new PNCounter(counter, new PNCounter.Builder(mockManagers.get(0)).decrement(-3L));
|
||||
Assert.assertEquals(2, (long) counter.value());
|
||||
|
||||
counter = new PNCounter(counter, new PNCounter.Builder(mockManagers.get(0)).decrement(-12L));
|
||||
Assert.assertEquals(14, (long) counter.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void networkLikeOperations() {
|
||||
PNCounter counter1 = new PNCounter(mockManagers.get(0));
|
||||
PNCounter counter2 = new PNCounter(mockManagers.get(1));
|
||||
PNCounter counter3 = new PNCounter(mockManagers.get(2));
|
||||
|
||||
Assert.assertEquals(0, (long) counter1.value());
|
||||
Assert.assertEquals(0, (long) counter2.value());
|
||||
Assert.assertEquals(0, (long) counter3.value());
|
||||
|
||||
counter1 = new PNCounter(counter1, new PNCounter.Builder(mockManagers.get(0)).increment(3L));
|
||||
Assert.assertEquals(3, (long) counter1.value());
|
||||
|
||||
counter2 = new PNCounter(counter2, new PNCounter.Builder(mockManagers.get(1)).increment(5L));
|
||||
Assert.assertEquals(5, (long) counter2.value());
|
||||
|
||||
counter3 = new PNCounter(counter3, new PNCounter.Builder(mockManagers.get(2)).decrement(7L));
|
||||
Assert.assertEquals(-7, (long) counter3.value());
|
||||
|
||||
// 2 becomes 2 and 1
|
||||
counter2 = counter2.merge(counter1);
|
||||
Assert.assertEquals(8, (long) counter2.value());
|
||||
|
||||
// 3 becomes 3 and 1
|
||||
counter3 = counter3.merge(counter1);
|
||||
Assert.assertEquals(-4, (long) counter3.value());
|
||||
|
||||
// 3 becomes all
|
||||
counter3 = counter3.merge(counter2);
|
||||
Assert.assertEquals(1, (long) counter3.value());
|
||||
|
||||
// 2 becomes all - different order
|
||||
counter2 = counter2.merge(counter3);
|
||||
Assert.assertEquals(1, (long) counter3.value());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user