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
Showing
3 changed files
with
63 additions
and
34 deletions
... | @@ -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 | } | ... | ... |
-
Please register or login to post a comment