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
65 additions
and
36 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,11 +362,19 @@ public class EventuallyConsistentMapImpl<K, V> | ... | @@ -360,11 +362,19 @@ 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) { |
| 367 | - persistentStore.update(key, tombstone); | 373 | + if (tombstonesDisabled) { |
| 374 | + persistentStore.remove(key); | ||
| 375 | + } else { | ||
| 376 | + persistentStore.update(key, tombstone); | ||
| 377 | + } | ||
| 368 | } | 378 | } |
| 369 | } | 379 | } |
| 370 | return previousValue.get(); | 380 | return previousValue.get(); |
| ... | @@ -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)) | 510 | + .map(ControllerNode::id) |
| 503 | - .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