Madan Jampani
Committed by Gerrit Code Review

Misc fixes/improvments to ECMapImpl. Most notably:

- Fixed logic in determining random peer to do AE
- Fixed for logic for when to do active sync if lightWeightAE is disabled
- Fixed tracking of ECMap activity

Change-Id: I35da91d6ef684e16630be7bd5e518c8400debe14
...@@ -15,7 +15,9 @@ ...@@ -15,7 +15,9 @@
15 */ 15 */
16 package org.onosproject.store.ecmap; 16 package org.onosproject.store.ecmap;
17 17
18 +import com.google.common.collect.Collections2;
18 import com.google.common.collect.ImmutableList; 19 import com.google.common.collect.ImmutableList;
20 +import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.Lists; 21 import com.google.common.collect.Lists;
20 import com.google.common.collect.Maps; 22 import com.google.common.collect.Maps;
21 import com.google.common.collect.Sets; 23 import com.google.common.collect.Sets;
...@@ -97,6 +99,8 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -97,6 +99,8 @@ public class EventuallyConsistentMapImpl<K, V>
97 private final ExecutorService communicationExecutor; 99 private final ExecutorService communicationExecutor;
98 private final Map<NodeId, EventAccumulator> senderPending; 100 private final Map<NodeId, EventAccumulator> senderPending;
99 101
102 + private final String mapName;
103 +
100 private volatile boolean destroyed = false; 104 private volatile boolean destroyed = false;
101 private static final String ERROR_DESTROYED = " map is already destroyed"; 105 private static final String ERROR_DESTROYED = " map is already destroyed";
102 private final String destroyedMessage; 106 private final String destroyedMessage;
...@@ -157,6 +161,7 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -157,6 +161,7 @@ public class EventuallyConsistentMapImpl<K, V>
157 TimeUnit antiEntropyTimeUnit, 161 TimeUnit antiEntropyTimeUnit,
158 boolean convergeFaster, 162 boolean convergeFaster,
159 boolean persistent) { 163 boolean persistent) {
164 + this.mapName = mapName;
160 items = Maps.newConcurrentMap(); 165 items = Maps.newConcurrentMap();
161 senderPending = Maps.newConcurrentMap(); 166 senderPending = Maps.newConcurrentMap();
162 destroyedMessage = mapName + ERROR_DESTROYED; 167 destroyedMessage = mapName + ERROR_DESTROYED;
...@@ -284,7 +289,7 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -284,7 +289,7 @@ public class EventuallyConsistentMapImpl<K, V>
284 return items.values() 289 return items.values()
285 .stream() 290 .stream()
286 .filter(MapValue::isAlive) 291 .filter(MapValue::isAlive)
287 - .anyMatch(v -> v.get().equals(value)); 292 + .anyMatch(v -> value.equals(v.get()));
288 } 293 }
289 294
290 @Override 295 @Override
...@@ -303,7 +308,7 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -303,7 +308,7 @@ public class EventuallyConsistentMapImpl<K, V>
303 checkNotNull(value, ERROR_NULL_VALUE); 308 checkNotNull(value, ERROR_NULL_VALUE);
304 309
305 MapValue<V> newValue = new MapValue<>(value, timestampProvider.apply(key, value)); 310 MapValue<V> newValue = new MapValue<>(value, timestampProvider.apply(key, value));
306 - if (updateInternal(key, newValue)) { 311 + if (putInternal(key, newValue)) {
307 notifyPeers(new UpdateEntry<>(key, newValue), peerUpdateFunction.apply(key, value)); 312 notifyPeers(new UpdateEntry<>(key, newValue), peerUpdateFunction.apply(key, value));
308 notifyListeners(new EventuallyConsistentMapEvent<>(PUT, key, value)); 313 notifyListeners(new EventuallyConsistentMapEvent<>(PUT, key, value));
309 } 314 }
...@@ -313,16 +318,7 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -313,16 +318,7 @@ public class EventuallyConsistentMapImpl<K, V>
313 public V remove(K key) { 318 public V remove(K key) {
314 checkState(!destroyed, destroyedMessage); 319 checkState(!destroyed, destroyedMessage);
315 checkNotNull(key, ERROR_NULL_KEY); 320 checkNotNull(key, ERROR_NULL_KEY);
316 - // TODO prevent calls here if value is important for timestamp 321 + return removeAndNotify(key, null);
317 - MapValue<V> tombstone = MapValue.tombstone(timestampProvider.apply(key, null));
318 - MapValue<V> previousValue = removeInternal(key, Optional.empty(), tombstone);
319 - if (previousValue != null) {
320 - notifyPeers(new UpdateEntry<>(key, tombstone), peerUpdateFunction.apply(key, previousValue.get()));
321 - if (previousValue.isAlive()) {
322 - notifyListeners(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get()));
323 - }
324 - }
325 - return previousValue != null ? previousValue.get() : null;
326 } 322 }
327 323
328 @Override 324 @Override
...@@ -330,22 +326,28 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -330,22 +326,28 @@ public class EventuallyConsistentMapImpl<K, V>
330 checkState(!destroyed, destroyedMessage); 326 checkState(!destroyed, destroyedMessage);
331 checkNotNull(key, ERROR_NULL_KEY); 327 checkNotNull(key, ERROR_NULL_KEY);
332 checkNotNull(value, ERROR_NULL_VALUE); 328 checkNotNull(value, ERROR_NULL_VALUE);
329 + removeAndNotify(key, value);
330 + }
331 +
332 + private V removeAndNotify(K key, V value) {
333 MapValue<V> tombstone = MapValue.tombstone(timestampProvider.apply(key, value)); 333 MapValue<V> tombstone = MapValue.tombstone(timestampProvider.apply(key, value));
334 - MapValue<V> previousValue = removeInternal(key, Optional.of(value), tombstone); 334 + MapValue<V> previousValue = removeInternal(key, Optional.ofNullable(value), tombstone);
335 if (previousValue != null) { 335 if (previousValue != null) {
336 notifyPeers(new UpdateEntry<>(key, tombstone), peerUpdateFunction.apply(key, previousValue.get())); 336 notifyPeers(new UpdateEntry<>(key, tombstone), peerUpdateFunction.apply(key, previousValue.get()));
337 if (previousValue.isAlive()) { 337 if (previousValue.isAlive()) {
338 notifyListeners(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get())); 338 notifyListeners(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get()));
339 } 339 }
340 } 340 }
341 + return previousValue != null ? previousValue.get() : null;
341 } 342 }
342 343
343 private MapValue<V> removeInternal(K key, Optional<V> value, MapValue<V> tombstone) { 344 private MapValue<V> removeInternal(K key, Optional<V> value, MapValue<V> tombstone) {
344 checkState(!destroyed, destroyedMessage); 345 checkState(!destroyed, destroyedMessage);
345 checkNotNull(key, ERROR_NULL_KEY); 346 checkNotNull(key, ERROR_NULL_KEY);
346 checkNotNull(value, ERROR_NULL_VALUE); 347 checkNotNull(value, ERROR_NULL_VALUE);
347 -
348 checkState(tombstone.isTombstone()); 348 checkState(tombstone.isTombstone());
349 +
350 + counter.incrementCount();
349 AtomicBoolean updated = new AtomicBoolean(false); 351 AtomicBoolean updated = new AtomicBoolean(false);
350 AtomicReference<MapValue<V>> previousValue = new AtomicReference<>(); 352 AtomicReference<MapValue<V>> previousValue = new AtomicReference<>();
351 items.compute(key, (k, existing) -> { 353 items.compute(key, (k, existing) -> {
...@@ -360,13 +362,21 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -360,13 +362,21 @@ public class EventuallyConsistentMapImpl<K, V>
360 if (updated.get()) { 362 if (updated.get()) {
361 previousValue.set(existing); 363 previousValue.set(existing);
362 } 364 }
363 - return updated.get() ? tombstone : existing; 365 + if (updated.get()) {
366 + return tombstonesDisabled ? null : tombstone;
367 + } else {
368 + return existing;
369 + }
364 }); 370 });
365 if (updated.get()) { 371 if (updated.get()) {
366 if (persistent) { 372 if (persistent) {
373 + if (tombstonesDisabled) {
374 + persistentStore.remove(key);
375 + } else {
367 persistentStore.update(key, tombstone); 376 persistentStore.update(key, tombstone);
368 } 377 }
369 } 378 }
379 + }
370 return previousValue.get(); 380 return previousValue.get();
371 } 381 }
372 382
...@@ -393,11 +403,7 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -393,11 +403,7 @@ public class EventuallyConsistentMapImpl<K, V>
393 @Override 403 @Override
394 public Collection<V> values() { 404 public Collection<V> values() {
395 checkState(!destroyed, destroyedMessage); 405 checkState(!destroyed, destroyedMessage);
396 - return Maps.filterValues(items, MapValue::isAlive) 406 + return Collections2.transform(Maps.filterValues(items, MapValue::isAlive).values(), MapValue::get);
397 - .values()
398 - .stream()
399 - .map(MapValue::get)
400 - .collect(Collectors.toList());
401 } 407 }
402 408
403 @Override 409 @Override
...@@ -416,14 +422,16 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -416,14 +422,16 @@ public class EventuallyConsistentMapImpl<K, V>
416 * @param newValue proposed new value 422 * @param newValue proposed new value
417 * @return true if update happened; false if map already contains a more recent value for the key 423 * @return true if update happened; false if map already contains a more recent value for the key
418 */ 424 */
419 - private boolean updateInternal(K key, MapValue<V> newValue) { 425 + private boolean putInternal(K key, MapValue<V> newValue) {
426 + checkState(!destroyed, destroyedMessage);
427 + checkNotNull(key, ERROR_NULL_KEY);
428 + checkNotNull(newValue, ERROR_NULL_VALUE);
429 + checkState(newValue.isAlive());
430 + counter.incrementCount();
420 AtomicBoolean updated = new AtomicBoolean(false); 431 AtomicBoolean updated = new AtomicBoolean(false);
421 items.compute(key, (k, existing) -> { 432 items.compute(key, (k, existing) -> {
422 if (existing == null || newValue.isNewerThan(existing)) { 433 if (existing == null || newValue.isNewerThan(existing)) {
423 updated.set(true); 434 updated.set(true);
424 - if (newValue.isTombstone()) {
425 - return tombstonesDisabled ? null : newValue;
426 - }
427 return newValue; 435 return newValue;
428 } 436 }
429 return existing; 437 return existing;
...@@ -499,8 +507,8 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -499,8 +507,8 @@ public class EventuallyConsistentMapImpl<K, V>
499 private Optional<NodeId> pickRandomActivePeer() { 507 private Optional<NodeId> pickRandomActivePeer() {
500 List<NodeId> activePeers = clusterService.getNodes() 508 List<NodeId> activePeers = clusterService.getNodes()
501 .stream() 509 .stream()
502 - .filter(node -> !localNodeId.equals(node))
503 .map(ControllerNode::id) 510 .map(ControllerNode::id)
511 + .filter(id -> !localNodeId.equals(id))
504 .filter(id -> clusterService.getState(id) == ControllerNode.State.ACTIVE) 512 .filter(id -> clusterService.getState(id) == ControllerNode.State.ACTIVE)
505 .collect(Collectors.toList()); 513 .collect(Collectors.toList());
506 Collections.shuffle(activePeers); 514 Collections.shuffle(activePeers);
...@@ -519,9 +527,9 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -519,9 +527,9 @@ public class EventuallyConsistentMapImpl<K, V>
519 }); 527 });
520 } 528 }
521 529
522 -
523 private AntiEntropyAdvertisement<K> createAdvertisement() { 530 private AntiEntropyAdvertisement<K> createAdvertisement() {
524 - return new AntiEntropyAdvertisement<K>(localNodeId, Maps.transformValues(items, MapValue::digest)); 531 + return new AntiEntropyAdvertisement<K>(localNodeId,
532 + ImmutableMap.copyOf(Maps.transformValues(items, MapValue::digest)));
525 } 533 }
526 534
527 private void handleAntiEntropyAdvertisement(AntiEntropyAdvertisement<K> ad) { 535 private void handleAntiEntropyAdvertisement(AntiEntropyAdvertisement<K> ad) {
...@@ -529,13 +537,14 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -529,13 +537,14 @@ public class EventuallyConsistentMapImpl<K, V>
529 return; 537 return;
530 } 538 }
531 try { 539 try {
540 + log.debug("Received anti-entropy advertisement from {} for {} with {} entries in it",
541 + mapName, ad.sender(), ad.digest().size());
532 antiEntropyCheckLocalItems(ad).forEach(this::notifyListeners); 542 antiEntropyCheckLocalItems(ad).forEach(this::notifyListeners);
533 543
534 if (!lightweightAntiEntropy) { 544 if (!lightweightAntiEntropy) {
535 - Set<K> missingKeys = Sets.difference(items.keySet(), ad.digest().keySet()); 545 + // if remote ad has any entries that the local copy is missing, actively sync
536 - // if remote ad has something unknown, actively sync 546 + // TODO: Missing keys is not the way local copy can be behind.
537 - if (missingKeys.size() > 0) { 547 + if (Sets.difference(ad.digest().keySet(), items.keySet()).size() > 0) {
538 - // Send the advertisement back if this peer is out-of-sync
539 // TODO: Send ad for missing keys and for entries that are stale 548 // TODO: Send ad for missing keys and for entries that are stale
540 sendAdvertisementToPeer(ad.sender()); 549 sendAdvertisementToPeer(ad.sender());
541 } 550 }
...@@ -561,7 +570,9 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -561,7 +570,9 @@ public class EventuallyConsistentMapImpl<K, V>
561 // local value is more recent, push to sender 570 // local value is more recent, push to sender
562 queueUpdate(new UpdateEntry<>(key, localValue), ImmutableList.of(sender)); 571 queueUpdate(new UpdateEntry<>(key, localValue), ImmutableList.of(sender));
563 } 572 }
564 - if (remoteValueDigest != null && remoteValueDigest.isTombstone()) { 573 + if (remoteValueDigest != null
574 + && remoteValueDigest.isNewerThan(localValue.digest())
575 + && remoteValueDigest.isTombstone()) {
565 MapValue<V> previousValue = removeInternal(key, 576 MapValue<V> previousValue = removeInternal(key,
566 Optional.empty(), 577 Optional.empty(),
567 MapValue.tombstone(remoteValueDigest.timestamp())); 578 MapValue.tombstone(remoteValueDigest.timestamp()));
...@@ -582,10 +593,10 @@ public class EventuallyConsistentMapImpl<K, V> ...@@ -582,10 +593,10 @@ public class EventuallyConsistentMapImpl<K, V>
582 final MapValue<V> value = update.value(); 593 final MapValue<V> value = update.value();
583 if (value.isTombstone()) { 594 if (value.isTombstone()) {
584 MapValue<V> previousValue = removeInternal(key, Optional.empty(), value); 595 MapValue<V> previousValue = removeInternal(key, Optional.empty(), value);
585 - if (previousValue != null && previousValue.get() != null) { 596 + if (previousValue != null && previousValue.isAlive()) {
586 notifyListeners(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get())); 597 notifyListeners(new EventuallyConsistentMapEvent<>(REMOVE, key, previousValue.get()));
587 } 598 }
588 - } else if (updateInternal(key, value)) { 599 + } else if (putInternal(key, value)) {
589 notifyListeners(new EventuallyConsistentMapEvent<>(PUT, key, value.get())); 600 notifyListeners(new EventuallyConsistentMapEvent<>(PUT, key, value.get()));
590 } 601 }
591 }); 602 });
......
...@@ -75,6 +75,11 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> { ...@@ -75,6 +75,11 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> {
75 executor.submit(() -> updateInternal(key, value)); 75 executor.submit(() -> updateInternal(key, value));
76 } 76 }
77 77
78 + @Override
79 + public void remove(K key) {
80 + executor.submit(() -> removeInternal(key));
81 + }
82 +
78 private void updateInternal(K key, MapValue<V> newValue) { 83 private void updateInternal(K key, MapValue<V> newValue) {
79 byte[] keyBytes = serializer.encode(key); 84 byte[] keyBytes = serializer.encode(key);
80 85
...@@ -89,4 +94,10 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> { ...@@ -89,4 +94,10 @@ class MapDbPersistentStore<K, V> implements PersistentStore<K, V> {
89 }); 94 });
90 database.commit(); 95 database.commit();
91 } 96 }
97 +
98 + private void removeInternal(K key) {
99 + byte[] keyBytes = serializer.encode(key);
100 + items.remove(keyBytes);
101 + database.commit();
102 + }
92 } 103 }
......
...@@ -37,4 +37,11 @@ interface PersistentStore<K, V> { ...@@ -37,4 +37,11 @@ interface PersistentStore<K, V> {
37 * @param value the value 37 * @param value the value
38 */ 38 */
39 void update(K key, MapValue<V> value); 39 void update(K key, MapValue<V> value);
40 +
41 + /**
42 + * Removes a key from persistent store.
43 + *
44 + * @param key the key to remove
45 + */
46 + void remove(K key);
40 } 47 }
......