Madan Jampani

Simplified ECMap implmentation by merging items and tombstones maps

Change-Id: If4253722d91c35a7e57dec3c2fceb216d14a7314
1 -/*
2 - * Copyright 2015 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onosproject.store.ecmap;
17 -
18 -import java.util.Objects;
19 -
20 -import org.onosproject.store.Timestamp;
21 -
22 -import static com.google.common.base.Preconditions.checkNotNull;
23 -
24 -/**
25 - * Base class for events in an EventuallyConsistentMap.
26 - */
27 -public abstract class AbstractEntry<K, V> implements Comparable<AbstractEntry<K, V>> {
28 - private final K key;
29 - private final Timestamp timestamp;
30 -
31 - /**
32 - * Creates a new put entry.
33 - *
34 - * @param key key of the entry
35 - * @param timestamp timestamp of the put event
36 - */
37 - public AbstractEntry(K key, Timestamp timestamp) {
38 - this.key = checkNotNull(key);
39 - this.timestamp = checkNotNull(timestamp);
40 - }
41 -
42 - // Needed for serialization.
43 - @SuppressWarnings("unused")
44 - protected AbstractEntry() {
45 - this.key = null;
46 - this.timestamp = null;
47 - }
48 -
49 - /**
50 - * Returns the key of the entry.
51 - *
52 - * @return the key
53 - */
54 - public K key() {
55 - return key;
56 - }
57 -
58 - /**
59 - * Returns the timestamp of the event.
60 - *
61 - * @return the timestamp
62 - */
63 - public Timestamp timestamp() {
64 - return timestamp;
65 - }
66 -
67 - @Override
68 - public int compareTo(AbstractEntry<K, V> o) {
69 - return this.timestamp.compareTo(o.timestamp);
70 - }
71 -
72 - @Override
73 - public int hashCode() {
74 - return Objects.hash(timestamp);
75 - }
76 -
77 - @Override
78 - public boolean equals(Object o) {
79 - if (this == o) {
80 - return true;
81 - }
82 - if (o instanceof AbstractEntry) {
83 - final AbstractEntry that = (AbstractEntry) o;
84 - return this.timestamp.equals(that.timestamp);
85 - }
86 - return false;
87 - }
88 -}
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
16 package org.onosproject.store.ecmap; 16 package org.onosproject.store.ecmap;
17 17
18 import com.google.common.base.MoreObjects; 18 import com.google.common.base.MoreObjects;
19 +import com.google.common.collect.ImmutableMap;
20 +
19 import org.onosproject.cluster.NodeId; 21 import org.onosproject.cluster.NodeId;
20 -import org.onosproject.store.Timestamp;
21 22
22 import java.util.Map; 23 import java.util.Map;
23 -
24 import static com.google.common.base.Preconditions.checkNotNull; 24 import static com.google.common.base.Preconditions.checkNotNull;
25 25
26 /** 26 /**
...@@ -29,22 +29,18 @@ import static com.google.common.base.Preconditions.checkNotNull; ...@@ -29,22 +29,18 @@ import static com.google.common.base.Preconditions.checkNotNull;
29 public class AntiEntropyAdvertisement<K> { 29 public class AntiEntropyAdvertisement<K> {
30 30
31 private final NodeId sender; 31 private final NodeId sender;
32 - private final Map<K, Timestamp> timestamps; 32 + private final Map<K, MapValue.Digest> digest;
33 - private final Map<K, Timestamp> tombstones;
34 33
35 /** 34 /**
36 * Creates a new anti entropy advertisement message. 35 * Creates a new anti entropy advertisement message.
37 * 36 *
38 * @param sender the sender's node ID 37 * @param sender the sender's node ID
39 - * @param timestamps map of item key to timestamp for current items 38 + * @param digest for map entries
40 - * @param tombstones map of item key to timestamp for removed items
41 */ 39 */
42 public AntiEntropyAdvertisement(NodeId sender, 40 public AntiEntropyAdvertisement(NodeId sender,
43 - Map<K, Timestamp> timestamps, 41 + Map<K, MapValue.Digest> digest) {
44 - Map<K, Timestamp> tombstones) {
45 this.sender = checkNotNull(sender); 42 this.sender = checkNotNull(sender);
46 - this.timestamps = checkNotNull(timestamps); 43 + this.digest = ImmutableMap.copyOf(checkNotNull(digest));
47 - this.tombstones = checkNotNull(tombstones);
48 } 44 }
49 45
50 /** 46 /**
...@@ -57,36 +53,19 @@ public class AntiEntropyAdvertisement<K> { ...@@ -57,36 +53,19 @@ public class AntiEntropyAdvertisement<K> {
57 } 53 }
58 54
59 /** 55 /**
60 - * Returns the map of current item timestamps. 56 + * Returns the digest for map entries.
61 * 57 *
62 - * @return current item timestamps 58 + * @return mapping from key to associated digest
63 */ 59 */
64 - public Map<K, Timestamp> timestamps() { 60 + public Map<K, MapValue.Digest> digest() {
65 - return timestamps; 61 + return digest;
66 - }
67 -
68 - /**
69 - * Returns the map of removed item timestamps.
70 - *
71 - * @return removed item timestamps
72 - */
73 - public Map<K, Timestamp> tombstones() {
74 - return tombstones;
75 - }
76 -
77 - // For serializer
78 - @SuppressWarnings("unused")
79 - private AntiEntropyAdvertisement() {
80 - this.sender = null;
81 - this.timestamps = null;
82 - this.tombstones = null;
83 } 62 }
84 63
85 @Override 64 @Override
86 public String toString() { 65 public String toString() {
87 return MoreObjects.toStringHelper(getClass()) 66 return MoreObjects.toStringHelper(getClass())
88 - .add("timestampsSize", timestamps.size()) 67 + .add("sender", sender)
89 - .add("tombstonesSize", tombstones.size()) 68 + .add("totalEntries", digest.size())
90 .toString(); 69 .toString();
91 } 70 }
92 } 71 }
......
...@@ -16,13 +16,10 @@ ...@@ -16,13 +16,10 @@
16 16
17 package org.onosproject.store.ecmap; 17 package org.onosproject.store.ecmap;
18 18
19 -import org.apache.commons.lang3.mutable.MutableBoolean;
20 import org.mapdb.DB; 19 import org.mapdb.DB;
21 import org.mapdb.DBMaker; 20 import org.mapdb.DBMaker;
22 import org.mapdb.Hasher; 21 import org.mapdb.Hasher;
23 import org.mapdb.Serializer; 22 import org.mapdb.Serializer;
24 -import org.onosproject.store.Timestamp;
25 -import org.onosproject.store.impl.Timestamped;
26 import org.onosproject.store.serializers.KryoSerializer; 23 import org.onosproject.store.serializers.KryoSerializer;
27 24
28 import java.io.File; 25 import java.io.File;
...@@ -42,7 +39,6 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> { ...@@ -42,7 +39,6 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> {
42 private final DB database; 39 private final DB database;
43 40
44 private final Map<byte[], byte[]> items; 41 private final Map<byte[], byte[]> items;
45 - private final Map<byte[], byte[]> tombstones;
46 42
47 /** 43 /**
48 * Creates a new MapDB based persistent store. 44 * Creates a new MapDB based persistent store.
...@@ -65,102 +61,32 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> { ...@@ -65,102 +61,32 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> {
65 .valueSerializer(Serializer.BYTE_ARRAY) 61 .valueSerializer(Serializer.BYTE_ARRAY)
66 .hasher(Hasher.BYTE_ARRAY) 62 .hasher(Hasher.BYTE_ARRAY)
67 .makeOrGet(); 63 .makeOrGet();
68 -
69 - tombstones = database.createHashMap("tombstones")
70 - .keySerializer(Serializer.BYTE_ARRAY)
71 - .valueSerializer(Serializer.BYTE_ARRAY)
72 - .hasher(Hasher.BYTE_ARRAY)
73 - .makeOrGet();
74 } 64 }
75 65
76 @Override 66 @Override
77 - public void readInto(Map<K, Timestamped<V>> items, Map<K, Timestamp> tombstones) { 67 + public void readInto(Map<K, MapValue<V>> items) {
78 this.items.forEach((keyBytes, valueBytes) -> 68 this.items.forEach((keyBytes, valueBytes) ->
79 items.put(serializer.decode(keyBytes), 69 items.put(serializer.decode(keyBytes),
80 serializer.decode(valueBytes))); 70 serializer.decode(valueBytes)));
81 -
82 - this.tombstones.forEach((keyBytes, valueBytes) ->
83 - tombstones.put(serializer.decode(keyBytes),
84 - serializer.decode(valueBytes)));
85 } 71 }
86 72
87 @Override 73 @Override
88 - public void put(K key, V value, Timestamp timestamp) { 74 + public void update(K key, MapValue<V> value) {
89 - executor.submit(() -> putInternal(key, value, timestamp)); 75 + executor.submit(() -> updateInternal(key, value));
90 } 76 }
91 77
92 - private void putInternal(K key, V value, Timestamp timestamp) { 78 + private void updateInternal(K key, MapValue<V> newValue) {
93 byte[] keyBytes = serializer.encode(key); 79 byte[] keyBytes = serializer.encode(key);
94 - byte[] removedBytes = tombstones.get(keyBytes);
95 -
96 - Timestamp removed = removedBytes == null ? null :
97 - serializer.decode(removedBytes);
98 - if (removed != null && removed.isNewerThan(timestamp)) {
99 - return;
100 - }
101 -
102 - final MutableBoolean updated = new MutableBoolean(false);
103 80
104 items.compute(keyBytes, (k, existingBytes) -> { 81 items.compute(keyBytes, (k, existingBytes) -> {
105 - Timestamped<V> existing = existingBytes == null ? null : 82 + MapValue<V> existing = existingBytes == null ? null :
106 serializer.decode(existingBytes); 83 serializer.decode(existingBytes);
107 - if (existing != null && existing.isNewerThan(timestamp)) { 84 + if (existing == null || newValue.isNewerThan(existing)) {
108 - updated.setFalse(); 85 + return serializer.encode(newValue);
109 - return existingBytes;
110 } else { 86 } else {
111 - updated.setTrue();
112 - return serializer.encode(new Timestamped<>(value, timestamp));
113 - }
114 - });
115 -
116 - boolean success = updated.booleanValue();
117 -
118 - if (success && removed != null) {
119 - tombstones.remove(keyBytes, removedBytes);
120 - }
121 -
122 - database.commit();
123 - }
124 -
125 - @Override
126 - public void remove(K key, Timestamp timestamp) {
127 - executor.submit(() -> removeInternal(key, timestamp));
128 - }
129 -
130 - private void removeInternal(K key, Timestamp timestamp) {
131 - byte[] keyBytes = serializer.encode(key);
132 -
133 - final MutableBoolean updated = new MutableBoolean(false);
134 -
135 - items.compute(keyBytes, (k, existingBytes) -> {
136 - Timestamp existing = existingBytes == null ? null :
137 - serializer.decode(existingBytes);
138 - if (existing != null && existing.isNewerThan(timestamp)) {
139 - updated.setFalse();
140 return existingBytes; 87 return existingBytes;
141 - } else {
142 - updated.setTrue();
143 - // remove from items map
144 - return null;
145 } 88 }
146 }); 89 });
147 -
148 - if (!updated.booleanValue()) {
149 - return;
150 - }
151 -
152 - byte[] timestampBytes = serializer.encode(timestamp);
153 - byte[] removedBytes = tombstones.get(keyBytes);
154 -
155 - Timestamp removedTimestamp = removedBytes == null ? null :
156 - serializer.decode(removedBytes);
157 - if (removedTimestamp == null) {
158 - tombstones.putIfAbsent(keyBytes, timestampBytes);
159 - } else if (timestamp.isNewerThan(removedTimestamp)) {
160 - tombstones.replace(keyBytes, removedBytes, timestampBytes);
161 - }
162 -
163 database.commit(); 90 database.commit();
164 } 91 }
165 -
166 } 92 }
......
1 +package org.onosproject.store.ecmap;
2 +
3 +import org.onosproject.store.Timestamp;
4 +import com.google.common.base.MoreObjects;
5 +
6 +/**
7 + * Representation of a value in EventuallyConsistentMap.
8 + *
9 + * @param <V> value type
10 + */
11 +public class MapValue<V> implements Comparable<MapValue<V>> {
12 + private final Timestamp timestamp;
13 + private final V value;
14 +
15 + public MapValue(V value, Timestamp timestamp) {
16 + this.value = value;
17 + this.timestamp = timestamp;
18 + }
19 +
20 + public boolean isTombstone() {
21 + return value == null;
22 + }
23 +
24 + public boolean isAlive() {
25 + return value != null;
26 + }
27 +
28 + public Timestamp timestamp() {
29 + return timestamp;
30 + }
31 +
32 + public V get() {
33 + return value;
34 + }
35 +
36 + @Override
37 + public int compareTo(MapValue<V> o) {
38 + return this.timestamp.compareTo(o.timestamp);
39 + }
40 +
41 + public boolean isNewerThan(MapValue<V> other) {
42 + return timestamp.isNewerThan(other.timestamp);
43 + }
44 +
45 + public boolean isNewerThan(Timestamp timestamp) {
46 + return timestamp.isNewerThan(timestamp);
47 + }
48 +
49 + public Digest digest() {
50 + return new Digest(timestamp, isTombstone());
51 + }
52 +
53 + @Override
54 + public String toString() {
55 + return MoreObjects.toStringHelper(getClass())
56 + .add("timestamp", timestamp)
57 + .add("value", value)
58 + .toString();
59 + }
60 +
61 + @SuppressWarnings("unused")
62 + private MapValue() {
63 + this.timestamp = null;
64 + this.value = null;
65 + }
66 +
67 + /**
68 + * Digest or summary of a MapValue for use during Anti-Entropy exchanges.
69 + */
70 + public static class Digest {
71 + private final Timestamp timestamp;
72 + private final boolean isTombstone;
73 +
74 + public Digest(Timestamp timestamp, boolean isTombstone) {
75 + this.timestamp = timestamp;
76 + this.isTombstone = isTombstone;
77 + }
78 +
79 + public Timestamp timestamp() {
80 + return timestamp;
81 + }
82 +
83 + public boolean isTombstone() {
84 + return isTombstone;
85 + }
86 +
87 + public boolean isNewerThan(Digest other) {
88 + return timestamp.isNewerThan(other.timestamp);
89 + }
90 +
91 + @Override
92 + public String toString() {
93 + return MoreObjects.toStringHelper(getClass())
94 + .add("timestamp", timestamp)
95 + .add("isTombstone", isTombstone)
96 + .toString();
97 + }
98 + }
99 +}
...@@ -16,9 +16,6 @@ ...@@ -16,9 +16,6 @@
16 16
17 package org.onosproject.store.ecmap; 17 package org.onosproject.store.ecmap;
18 18
19 -import org.onosproject.store.Timestamp;
20 -import org.onosproject.store.impl.Timestamped;
21 -
22 import java.util.Map; 19 import java.util.Map;
23 20
24 /** 21 /**
...@@ -30,24 +27,14 @@ interface PersistentStore<K, V> { ...@@ -30,24 +27,14 @@ interface PersistentStore<K, V> {
30 * Read the contents of the disk into the given maps. 27 * Read the contents of the disk into the given maps.
31 * 28 *
32 * @param items items map 29 * @param items items map
33 - * @param tombstones tombstones map
34 */ 30 */
35 - void readInto(Map<K, Timestamped<V>> items, Map<K, Timestamp> tombstones); 31 + void readInto(Map<K, MapValue<V>> items);
36 32
37 /** 33 /**
38 - * Puts a new key,value pair into the map on disk. 34 + * Updates a key,value pair in the persistent store.
39 * 35 *
40 * @param key the key 36 * @param key the key
41 * @param value the value 37 * @param value the value
42 - * @param timestamp the timestamp of the update
43 - */
44 - void put(K key, V value, Timestamp timestamp);
45 -
46 - /**
47 - * Removes a key from the map on disk.
48 - *
49 - * @param key the key
50 - * @param timestamp the timestamp of the update
51 */ 38 */
52 - void remove(K key, Timestamp timestamp); 39 + void update(K key, MapValue<V> value);
53 } 40 }
......
1 -/*
2 - * Copyright 2015 Open Networking Laboratory
3 - *
4 - * Licensed under the Apache License, Version 2.0 (the "License");
5 - * you may not use this file except in compliance with the License.
6 - * You may obtain a copy of the License at
7 - *
8 - * http://www.apache.org/licenses/LICENSE-2.0
9 - *
10 - * Unless required by applicable law or agreed to in writing, software
11 - * distributed under the License is distributed on an "AS IS" BASIS,
12 - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 - * See the License for the specific language governing permissions and
14 - * limitations under the License.
15 - */
16 -package org.onosproject.store.ecmap;
17 -
18 -import com.google.common.base.MoreObjects;
19 -import org.onosproject.store.Timestamp;
20 -
21 -/**
22 - * Describes a single remove event in an EventuallyConsistentMap.
23 - */
24 -final class RemoveEntry<K, V> extends AbstractEntry<K, V> {
25 - /**
26 - * Creates a new remove entry.
27 - *
28 - * @param key key of the entry
29 - * @param timestamp timestamp of the remove event
30 - */
31 - public RemoveEntry(K key, Timestamp timestamp) {
32 - super(key, timestamp);
33 - }
34 -
35 - // Needed for serialization.
36 - @SuppressWarnings("unused")
37 - private RemoveEntry() {
38 - super();
39 - }
40 -
41 - @Override
42 - public String toString() {
43 - return MoreObjects.toStringHelper(getClass())
44 - .add("key", key())
45 - .add("timestamp", timestamp())
46 - .toString();
47 - }
48 -}
...@@ -15,34 +15,35 @@ ...@@ -15,34 +15,35 @@
15 */ 15 */
16 package org.onosproject.store.ecmap; 16 package org.onosproject.store.ecmap;
17 17
18 -import com.google.common.base.MoreObjects;
19 -import org.onosproject.store.Timestamp;
20 -
21 import static com.google.common.base.Preconditions.checkNotNull; 18 import static com.google.common.base.Preconditions.checkNotNull;
22 19
20 +import com.google.common.base.MoreObjects;
21 +
23 /** 22 /**
24 - * Describes a single put event in an EventuallyConsistentMap. 23 + * Describes a single update event in an EventuallyConsistentMap.
25 */ 24 */
26 -final class PutEntry<K, V> extends AbstractEntry<K, V> { 25 +final class UpdateEntry<K, V> implements Comparable<UpdateEntry<K, V>> {
27 - private final V value; 26 + private final K key;
27 + private final MapValue<V> value;
28 28
29 /** 29 /**
30 - * Creates a new put entry. 30 + * Creates a new update entry.
31 * 31 *
32 * @param key key of the entry 32 * @param key key of the entry
33 * @param value value of the entry 33 * @param value value of the entry
34 - * @param timestamp timestamp of the put event
35 */ 34 */
36 - public PutEntry(K key, V value, Timestamp timestamp) { 35 + public UpdateEntry(K key, MapValue<V> value) {
37 - super(key, timestamp); 36 + this.key = checkNotNull(key);
38 this.value = checkNotNull(value); 37 this.value = checkNotNull(value);
39 } 38 }
40 39
41 - // Needed for serialization. 40 + /**
42 - @SuppressWarnings("unused") 41 + * Returns the key.
43 - private PutEntry() { 42 + *
44 - super(); 43 + * @return the key
45 - this.value = null; 44 + */
45 + public K key() {
46 + return key;
46 } 47 }
47 48
48 /** 49 /**
...@@ -50,16 +51,26 @@ final class PutEntry<K, V> extends AbstractEntry<K, V> { ...@@ -50,16 +51,26 @@ final class PutEntry<K, V> extends AbstractEntry<K, V> {
50 * 51 *
51 * @return the value 52 * @return the value
52 */ 53 */
53 - public V value() { 54 + public MapValue<V> value() {
54 return value; 55 return value;
55 } 56 }
56 57
57 @Override 58 @Override
59 + public int compareTo(UpdateEntry<K, V> o) {
60 + return this.value.timestamp().compareTo(o.value.timestamp());
61 + }
62 +
63 + @Override
58 public String toString() { 64 public String toString() {
59 return MoreObjects.toStringHelper(getClass()) 65 return MoreObjects.toStringHelper(getClass())
60 .add("key", key()) 66 .add("key", key())
61 .add("value", value) 67 .add("value", value)
62 - .add("timestamp", timestamp())
63 .toString(); 68 .toString();
64 } 69 }
70 +
71 + @SuppressWarnings("unused")
72 + private UpdateEntry() {
73 + this.key = null;
74 + this.value = null;
75 + }
65 } 76 }
......
...@@ -16,8 +16,8 @@ ...@@ -16,8 +16,8 @@
16 package org.onosproject.store.ecmap; 16 package org.onosproject.store.ecmap;
17 17
18 import com.google.common.collect.ComparisonChain; 18 import com.google.common.collect.ComparisonChain;
19 +import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableSet; 20 import com.google.common.collect.ImmutableSet;
20 -import com.google.common.collect.Lists;
21 import com.google.common.util.concurrent.MoreExecutors; 21 import com.google.common.util.concurrent.MoreExecutors;
22 22
23 import org.junit.After; 23 import org.junit.After;
...@@ -32,7 +32,6 @@ import org.onosproject.cluster.NodeId; ...@@ -32,7 +32,6 @@ import org.onosproject.cluster.NodeId;
32 import org.onosproject.event.AbstractEvent; 32 import org.onosproject.event.AbstractEvent;
33 import org.onosproject.store.Timestamp; 33 import org.onosproject.store.Timestamp;
34 import org.onosproject.store.cluster.messaging.ClusterCommunicationService; 34 import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
35 -import org.onosproject.store.cluster.messaging.ClusterMessage;
36 import org.onosproject.store.cluster.messaging.ClusterMessageHandler; 35 import org.onosproject.store.cluster.messaging.ClusterMessageHandler;
37 import org.onosproject.store.cluster.messaging.MessageSubject; 36 import org.onosproject.store.cluster.messaging.MessageSubject;
38 import org.onosproject.store.impl.LogicalTimestamp; 37 import org.onosproject.store.impl.LogicalTimestamp;
...@@ -44,11 +43,13 @@ import org.onosproject.store.service.EventuallyConsistentMapEvent; ...@@ -44,11 +43,13 @@ import org.onosproject.store.service.EventuallyConsistentMapEvent;
44 import org.onosproject.store.service.EventuallyConsistentMapListener; 43 import org.onosproject.store.service.EventuallyConsistentMapListener;
45 44
46 import java.util.ArrayList; 45 import java.util.ArrayList;
46 +import java.util.Collection;
47 import java.util.HashMap; 47 import java.util.HashMap;
48 import java.util.HashSet; 48 import java.util.HashSet;
49 import java.util.List; 49 import java.util.List;
50 import java.util.Map; 50 import java.util.Map;
51 import java.util.Objects; 51 import java.util.Objects;
52 +import java.util.Optional;
52 import java.util.Set; 53 import java.util.Set;
53 import java.util.concurrent.CompletableFuture; 54 import java.util.concurrent.CompletableFuture;
54 import java.util.concurrent.CountDownLatch; 55 import java.util.concurrent.CountDownLatch;
...@@ -89,8 +90,8 @@ public class EventuallyConsistentMapImplTest { ...@@ -89,8 +90,8 @@ public class EventuallyConsistentMapImplTest {
89 private final ControllerNode self = 90 private final ControllerNode self =
90 new DefaultControllerNode(new NodeId("local"), IpAddress.valueOf(1)); 91 new DefaultControllerNode(new NodeId("local"), IpAddress.valueOf(1));
91 92
92 - private ClusterMessageHandler updateHandler; 93 + private Consumer<Collection<UpdateEntry<String, String>>> updateHandler;
93 - private ClusterMessageHandler antiEntropyHandler; 94 + private Consumer<AntiEntropyAdvertisement<String>> antiEntropyHandler;
94 95
95 /* 96 /*
96 * Serialization is a bit tricky here. We need to serialize in the tests 97 * Serialization is a bit tricky here. We need to serialize in the tests
...@@ -109,11 +110,10 @@ public class EventuallyConsistentMapImplTest { ...@@ -109,11 +110,10 @@ public class EventuallyConsistentMapImplTest {
109 // Below is the classes that the map internally registers 110 // Below is the classes that the map internally registers
110 .register(LogicalTimestamp.class) 111 .register(LogicalTimestamp.class)
111 .register(WallClockTimestamp.class) 112 .register(WallClockTimestamp.class)
112 - .register(PutEntry.class)
113 - .register(RemoveEntry.class)
114 .register(ArrayList.class) 113 .register(ArrayList.class)
115 .register(AntiEntropyAdvertisement.class) 114 .register(AntiEntropyAdvertisement.class)
116 .register(HashMap.class) 115 .register(HashMap.class)
116 + .register(Optional.class)
117 .build(); 117 .build();
118 } 118 }
119 }; 119 };
...@@ -131,9 +131,9 @@ public class EventuallyConsistentMapImplTest { ...@@ -131,9 +131,9 @@ public class EventuallyConsistentMapImplTest {
131 // delegate to our ClusterCommunicationService implementation. This 131 // delegate to our ClusterCommunicationService implementation. This
132 // allows us to get a reference to the map's internal cluster message 132 // allows us to get a reference to the map's internal cluster message
133 // handlers so we can induce events coming in from a peer. 133 // handlers so we can induce events coming in from a peer.
134 - clusterCommunicator.addSubscriber(anyObject(MessageSubject.class), 134 + clusterCommunicator.<String>addSubscriber(anyObject(MessageSubject.class),
135 - anyObject(ClusterMessageHandler.class), anyObject(ExecutorService.class)); 135 + anyObject(Function.class), anyObject(Consumer.class), anyObject(Executor.class));
136 - expectLastCall().andDelegateTo(new TestClusterCommunicationService()).times(3); 136 + expectLastCall().andDelegateTo(new TestClusterCommunicationService()).times(2);
137 137
138 replay(clusterCommunicator); 138 replay(clusterCommunicator);
139 139
...@@ -237,15 +237,15 @@ public class EventuallyConsistentMapImplTest { ...@@ -237,15 +237,15 @@ public class EventuallyConsistentMapImplTest {
237 assertEquals(VALUE1, ecMap.get(KEY1)); 237 assertEquals(VALUE1, ecMap.get(KEY1));
238 238
239 // Remote put 239 // Remote put
240 - ClusterMessage message 240 + List<UpdateEntry<String, String>> message
241 - = generatePutMessage(KEY2, VALUE2, clockService.getTimestamp(KEY2, VALUE2)); 241 + = ImmutableList.of(generatePutMessage(KEY2, VALUE2, clockService.getTimestamp(KEY2, VALUE2)));
242 242
243 // Create a latch so we know when the put operation has finished 243 // Create a latch so we know when the put operation has finished
244 latch = new CountDownLatch(1); 244 latch = new CountDownLatch(1);
245 ecMap.addListener(new TestListener(latch)); 245 ecMap.addListener(new TestListener(latch));
246 246
247 assertNull(ecMap.get(KEY2)); 247 assertNull(ecMap.get(KEY2));
248 - updateHandler.handle(message); 248 + updateHandler.accept(message);
249 assertTrue("External listener never got notified of internal event", 249 assertTrue("External listener never got notified of internal event",
250 latch.await(100, TimeUnit.MILLISECONDS)); 250 latch.await(100, TimeUnit.MILLISECONDS));
251 assertEquals(VALUE2, ecMap.get(KEY2)); 251 assertEquals(VALUE2, ecMap.get(KEY2));
...@@ -255,14 +255,13 @@ public class EventuallyConsistentMapImplTest { ...@@ -255,14 +255,13 @@ public class EventuallyConsistentMapImplTest {
255 assertNull(ecMap.get(KEY2)); 255 assertNull(ecMap.get(KEY2));
256 256
257 // Remote remove 257 // Remote remove
258 - ClusterMessage removeMessage 258 + message = ImmutableList.of(generateRemoveMessage(KEY1, clockService.getTimestamp(KEY1, VALUE1)));
259 - = generateRemoveMessage(KEY1, clockService.getTimestamp(KEY1, VALUE1));
260 259
261 // Create a latch so we know when the remove operation has finished 260 // Create a latch so we know when the remove operation has finished
262 latch = new CountDownLatch(1); 261 latch = new CountDownLatch(1);
263 ecMap.addListener(new TestListener(latch)); 262 ecMap.addListener(new TestListener(latch));
264 263
265 - updateHandler.handle(removeMessage); 264 + updateHandler.accept(message);
266 assertTrue("External listener never got notified of internal event", 265 assertTrue("External listener never got notified of internal event",
267 latch.await(100, TimeUnit.MILLISECONDS)); 266 latch.await(100, TimeUnit.MILLISECONDS));
268 assertNull(ecMap.get(KEY1)); 267 assertNull(ecMap.get(KEY1));
...@@ -601,49 +600,35 @@ public class EventuallyConsistentMapImplTest { ...@@ -601,49 +600,35 @@ public class EventuallyConsistentMapImplTest {
601 } 600 }
602 } 601 }
603 602
604 - private ClusterMessage generatePutMessage(String key, String value, Timestamp timestamp) { 603 + private UpdateEntry<String, String> generatePutMessage(String key, String value, Timestamp timestamp) {
605 - PutEntry<String, String> event = new PutEntry<>(key, value, timestamp); 604 + return new UpdateEntry<>(key, new MapValue<>(value, timestamp));
606 -
607 - return new ClusterMessage(
608 - clusterService.getLocalNode().id(), UPDATE_MESSAGE_SUBJECT,
609 - SERIALIZER.encode(Lists.newArrayList(event)));
610 } 605 }
611 606
612 - private List<PutEntry<String, String>> generatePutMessage( 607 + private List<UpdateEntry<String, String>> generatePutMessage(
613 String key1, String value1, String key2, String value2) { 608 String key1, String value1, String key2, String value2) {
614 - ArrayList<PutEntry<String, String>> list = new ArrayList<>(); 609 + List<UpdateEntry<String, String>> list = new ArrayList<>();
615 610
616 Timestamp timestamp1 = clockService.peek(1); 611 Timestamp timestamp1 = clockService.peek(1);
617 Timestamp timestamp2 = clockService.peek(2); 612 Timestamp timestamp2 = clockService.peek(2);
618 613
619 - PutEntry<String, String> pe1 = new PutEntry<>(key1, value1, timestamp1); 614 + list.add(generatePutMessage(key1, value1, timestamp1));
620 - PutEntry<String, String> pe2 = new PutEntry<>(key2, value2, timestamp2); 615 + list.add(generatePutMessage(key2, value2, timestamp2));
621 -
622 - list.add(pe1);
623 - list.add(pe2);
624 616
625 return list; 617 return list;
626 } 618 }
627 619
628 - private ClusterMessage generateRemoveMessage(String key, Timestamp timestamp) { 620 + private UpdateEntry<String, String> generateRemoveMessage(String key, Timestamp timestamp) {
629 - RemoveEntry<String, String> event = new RemoveEntry<>(key, timestamp); 621 + return new UpdateEntry<>(key, new MapValue<>(null, timestamp));
630 -
631 - return new ClusterMessage(
632 - clusterService.getLocalNode().id(), UPDATE_MESSAGE_SUBJECT,
633 - SERIALIZER.encode(Lists.newArrayList(event)));
634 } 622 }
635 623
636 - private List<RemoveEntry<String, String>> generateRemoveMessage(String key1, String key2) { 624 + private List<UpdateEntry<String, String>> generateRemoveMessage(String key1, String key2) {
637 - ArrayList<RemoveEntry<String, String>> list = new ArrayList<>(); 625 + List<UpdateEntry<String, String>> list = new ArrayList<>();
638 626
639 Timestamp timestamp1 = clockService.peek(1); 627 Timestamp timestamp1 = clockService.peek(1);
640 Timestamp timestamp2 = clockService.peek(2); 628 Timestamp timestamp2 = clockService.peek(2);
641 629
642 - RemoveEntry<String, String> re1 = new RemoveEntry<>(key1, timestamp1); 630 + list.add(generateRemoveMessage(key1, timestamp1));
643 - RemoveEntry<String, String> re2 = new RemoveEntry<>(key2, timestamp2); 631 + list.add(generateRemoveMessage(key2, timestamp2));
644 -
645 - list.add(re1);
646 - list.add(re2);
647 632
648 return list; 633 return list;
649 } 634 }
...@@ -737,13 +722,6 @@ public class EventuallyConsistentMapImplTest { ...@@ -737,13 +722,6 @@ public class EventuallyConsistentMapImplTest {
737 public void addSubscriber(MessageSubject subject, 722 public void addSubscriber(MessageSubject subject,
738 ClusterMessageHandler subscriber, 723 ClusterMessageHandler subscriber,
739 ExecutorService executor) { 724 ExecutorService executor) {
740 - if (subject.equals(UPDATE_MESSAGE_SUBJECT)) {
741 - updateHandler = subscriber;
742 - } else if (subject.equals(ANTI_ENTROPY_MESSAGE_SUBJECT)) {
743 - antiEntropyHandler = subscriber;
744 - } else {
745 - throw new RuntimeException("Unexpected message subject " + subject.toString());
746 - }
747 } 725 }
748 726
749 @Override 727 @Override
...@@ -793,6 +771,13 @@ public class EventuallyConsistentMapImplTest { ...@@ -793,6 +771,13 @@ public class EventuallyConsistentMapImplTest {
793 public <M> void addSubscriber(MessageSubject subject, 771 public <M> void addSubscriber(MessageSubject subject,
794 Function<byte[], M> decoder, Consumer<M> handler, 772 Function<byte[], M> decoder, Consumer<M> handler,
795 Executor executor) { 773 Executor executor) {
774 + if (subject.equals(UPDATE_MESSAGE_SUBJECT)) {
775 + updateHandler = (Consumer<Collection<UpdateEntry<String, String>>>) handler;
776 + } else if (subject.equals(ANTI_ENTROPY_MESSAGE_SUBJECT)) {
777 + antiEntropyHandler = (Consumer<AntiEntropyAdvertisement<String>>) handler;
778 + } else {
779 + throw new RuntimeException("Unexpected message subject " + subject.toString());
780 + }
796 } 781 }
797 } 782 }
798 783
......