Simplified ECMap implmentation by merging items and tombstones maps
Change-Id: If4253722d91c35a7e57dec3c2fceb216d14a7314
Showing
9 changed files
with
182 additions
and
331 deletions
| 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 | } | ... | ... |
This diff is collapsed. Click to expand it.
| ... | @@ -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 | ... | ... |
-
Please register or login to post a comment