GossipLinkStore AE bugfix + cleanup
Change-Id: If4cbaa65f980f10713488e6bf1be5d01c3131780
Showing
1 changed file
with
146 additions
and
80 deletions
1 | package org.onlab.onos.store.link.impl; | 1 | package org.onlab.onos.store.link.impl; |
2 | 2 | ||
3 | import com.google.common.base.Function; | 3 | import com.google.common.base.Function; |
4 | -import com.google.common.base.Predicate; | ||
5 | import com.google.common.collect.FluentIterable; | 4 | import com.google.common.collect.FluentIterable; |
6 | import com.google.common.collect.HashMultimap; | 5 | import com.google.common.collect.HashMultimap; |
7 | import com.google.common.collect.ImmutableList; | 6 | import com.google.common.collect.ImmutableList; |
... | @@ -27,7 +26,6 @@ import org.onlab.onos.net.Link; | ... | @@ -27,7 +26,6 @@ import org.onlab.onos.net.Link; |
27 | import org.onlab.onos.net.SparseAnnotations; | 26 | import org.onlab.onos.net.SparseAnnotations; |
28 | import org.onlab.onos.net.Link.Type; | 27 | import org.onlab.onos.net.Link.Type; |
29 | import org.onlab.onos.net.LinkKey; | 28 | import org.onlab.onos.net.LinkKey; |
30 | -import org.onlab.onos.net.Provided; | ||
31 | import org.onlab.onos.net.device.DeviceClockService; | 29 | import org.onlab.onos.net.device.DeviceClockService; |
32 | import org.onlab.onos.net.link.DefaultLinkDescription; | 30 | import org.onlab.onos.net.link.DefaultLinkDescription; |
33 | import org.onlab.onos.net.link.LinkDescription; | 31 | import org.onlab.onos.net.link.LinkDescription; |
... | @@ -70,7 +68,9 @@ import static org.onlab.onos.net.link.LinkEvent.Type.*; | ... | @@ -70,7 +68,9 @@ import static org.onlab.onos.net.link.LinkEvent.Type.*; |
70 | import static org.onlab.util.Tools.namedThreads; | 68 | import static org.onlab.util.Tools.namedThreads; |
71 | import static org.slf4j.LoggerFactory.getLogger; | 69 | import static org.slf4j.LoggerFactory.getLogger; |
72 | import static com.google.common.collect.Multimaps.synchronizedSetMultimap; | 70 | import static com.google.common.collect.Multimaps.synchronizedSetMultimap; |
71 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
73 | import static com.google.common.base.Predicates.notNull; | 72 | import static com.google.common.base.Predicates.notNull; |
73 | +import static org.onlab.onos.store.link.impl.GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT; | ||
74 | 74 | ||
75 | /** | 75 | /** |
76 | * Manages inventory of infrastructure links in distributed data store | 76 | * Manages inventory of infrastructure links in distributed data store |
... | @@ -239,9 +239,9 @@ public class GossipLinkStore | ... | @@ -239,9 +239,9 @@ public class GossipLinkStore |
239 | LinkKey key = linkKey(linkDescription.src(), linkDescription.dst()); | 239 | LinkKey key = linkKey(linkDescription.src(), linkDescription.dst()); |
240 | final LinkEvent event; | 240 | final LinkEvent event; |
241 | final Timestamped<LinkDescription> mergedDesc; | 241 | final Timestamped<LinkDescription> mergedDesc; |
242 | - synchronized (getLinkDescriptions(key)) { | 242 | + synchronized (getOrCreateLinkDescriptions(key)) { |
243 | event = createOrUpdateLinkInternal(providerId, deltaDesc); | 243 | event = createOrUpdateLinkInternal(providerId, deltaDesc); |
244 | - mergedDesc = getLinkDescriptions(key).get(providerId); | 244 | + mergedDesc = getOrCreateLinkDescriptions(key).get(providerId); |
245 | } | 245 | } |
246 | 246 | ||
247 | if (event != null) { | 247 | if (event != null) { |
... | @@ -265,7 +265,7 @@ public class GossipLinkStore | ... | @@ -265,7 +265,7 @@ public class GossipLinkStore |
265 | 265 | ||
266 | LinkKey key = linkKey(linkDescription.value().src(), | 266 | LinkKey key = linkKey(linkDescription.value().src(), |
267 | linkDescription.value().dst()); | 267 | linkDescription.value().dst()); |
268 | - Map<ProviderId, Timestamped<LinkDescription>> descs = getLinkDescriptions(key); | 268 | + Map<ProviderId, Timestamped<LinkDescription>> descs = getOrCreateLinkDescriptions(key); |
269 | 269 | ||
270 | synchronized (descs) { | 270 | synchronized (descs) { |
271 | // if the link was previously removed, we should proceed if and | 271 | // if the link was previously removed, we should proceed if and |
... | @@ -296,7 +296,7 @@ public class GossipLinkStore | ... | @@ -296,7 +296,7 @@ public class GossipLinkStore |
296 | ProviderId providerId, | 296 | ProviderId providerId, |
297 | Timestamped<LinkDescription> linkDescription) { | 297 | Timestamped<LinkDescription> linkDescription) { |
298 | 298 | ||
299 | - // merge existing attributes and merge | 299 | + // merge existing annotations |
300 | Timestamped<LinkDescription> existingLinkDescription = descs.get(providerId); | 300 | Timestamped<LinkDescription> existingLinkDescription = descs.get(providerId); |
301 | if (existingLinkDescription != null && existingLinkDescription.isNewer(linkDescription)) { | 301 | if (existingLinkDescription != null && existingLinkDescription.isNewer(linkDescription)) { |
302 | return null; | 302 | return null; |
... | @@ -377,14 +377,54 @@ public class GossipLinkStore | ... | @@ -377,14 +377,54 @@ public class GossipLinkStore |
377 | return event; | 377 | return event; |
378 | } | 378 | } |
379 | 379 | ||
380 | + private static Timestamped<LinkDescription> getPrimaryDescription( | ||
381 | + Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) { | ||
382 | + | ||
383 | + synchronized (linkDescriptions) { | ||
384 | + for (Entry<ProviderId, Timestamped<LinkDescription>> | ||
385 | + e : linkDescriptions.entrySet()) { | ||
386 | + | ||
387 | + if (!e.getKey().isAncillary()) { | ||
388 | + return e.getValue(); | ||
389 | + } | ||
390 | + } | ||
391 | + } | ||
392 | + return null; | ||
393 | + } | ||
394 | + | ||
395 | + | ||
396 | + // TODO: consider slicing out as Timestamp utils | ||
397 | + /** | ||
398 | + * Checks is timestamp is more recent than timestamped object. | ||
399 | + * | ||
400 | + * @param timestamp to check if this is more recent then other | ||
401 | + * @param timestamped object to be tested against | ||
402 | + * @return true if {@code timestamp} is more recent than {@code timestamped} | ||
403 | + * or {@code timestamped is null} | ||
404 | + */ | ||
405 | + private static boolean isMoreRecent(Timestamp timestamp, Timestamped<?> timestamped) { | ||
406 | + checkNotNull(timestamp); | ||
407 | + if (timestamped == null) { | ||
408 | + return true; | ||
409 | + } | ||
410 | + return timestamp.compareTo(timestamped.timestamp()) > 0; | ||
411 | + } | ||
412 | + | ||
380 | private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) { | 413 | private LinkEvent removeLinkInternal(LinkKey key, Timestamp timestamp) { |
381 | - Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions = | 414 | + Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions |
382 | - getLinkDescriptions(key); | 415 | + = getOrCreateLinkDescriptions(key); |
416 | + | ||
383 | synchronized (linkDescriptions) { | 417 | synchronized (linkDescriptions) { |
418 | + if (linkDescriptions.isEmpty()) { | ||
419 | + // never seen such link before. keeping timestamp for record | ||
420 | + removedLinks.put(key, timestamp); | ||
421 | + return null; | ||
422 | + } | ||
384 | // accept removal request if given timestamp is newer than | 423 | // accept removal request if given timestamp is newer than |
385 | // the latest Timestamp from Primary provider | 424 | // the latest Timestamp from Primary provider |
386 | - ProviderId primaryProviderId = pickPrimaryProviderId(linkDescriptions); | 425 | + Timestamped<LinkDescription> prim = getPrimaryDescription(linkDescriptions); |
387 | - if (linkDescriptions.get(primaryProviderId).isNewer(timestamp)) { | 426 | + if (!isMoreRecent(timestamp, prim)) { |
427 | + // outdated remove request, ignore | ||
388 | return null; | 428 | return null; |
389 | } | 429 | } |
390 | removedLinks.put(key, timestamp); | 430 | removedLinks.put(key, timestamp); |
... | @@ -406,12 +446,13 @@ public class GossipLinkStore | ... | @@ -406,12 +446,13 @@ public class GossipLinkStore |
406 | /** | 446 | /** |
407 | * @return primary ProviderID, or randomly chosen one if none exists | 447 | * @return primary ProviderID, or randomly chosen one if none exists |
408 | */ | 448 | */ |
409 | - private ProviderId pickPrimaryProviderId( | 449 | + private static ProviderId pickBaseProviderId( |
410 | Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) { | 450 | Map<ProviderId, Timestamped<LinkDescription>> linkDescriptions) { |
411 | 451 | ||
412 | ProviderId fallBackPrimary = null; | 452 | ProviderId fallBackPrimary = null; |
413 | for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) { | 453 | for (Entry<ProviderId, Timestamped<LinkDescription>> e : linkDescriptions.entrySet()) { |
414 | if (!e.getKey().isAncillary()) { | 454 | if (!e.getKey().isAncillary()) { |
455 | + // found primary | ||
415 | return e.getKey(); | 456 | return e.getKey(); |
416 | } else if (fallBackPrimary == null) { | 457 | } else if (fallBackPrimary == null) { |
417 | // pick randomly as a fallback in case there is no primary | 458 | // pick randomly as a fallback in case there is no primary |
... | @@ -421,9 +462,10 @@ public class GossipLinkStore | ... | @@ -421,9 +462,10 @@ public class GossipLinkStore |
421 | return fallBackPrimary; | 462 | return fallBackPrimary; |
422 | } | 463 | } |
423 | 464 | ||
465 | + // Guarded by linkDescs value (=locking each Link) | ||
424 | private Link composeLink(Map<ProviderId, Timestamped<LinkDescription>> descs) { | 466 | private Link composeLink(Map<ProviderId, Timestamped<LinkDescription>> descs) { |
425 | - ProviderId primaryProviderId = pickPrimaryProviderId(descs); | 467 | + ProviderId baseProviderId = pickBaseProviderId(descs); |
426 | - Timestamped<LinkDescription> base = descs.get(primaryProviderId); | 468 | + Timestamped<LinkDescription> base = descs.get(baseProviderId); |
427 | 469 | ||
428 | ConnectPoint src = base.value().src(); | 470 | ConnectPoint src = base.value().src(); |
429 | ConnectPoint dst = base.value().dst(); | 471 | ConnectPoint dst = base.value().dst(); |
... | @@ -432,7 +474,7 @@ public class GossipLinkStore | ... | @@ -432,7 +474,7 @@ public class GossipLinkStore |
432 | annotations = merge(annotations, base.value().annotations()); | 474 | annotations = merge(annotations, base.value().annotations()); |
433 | 475 | ||
434 | for (Entry<ProviderId, Timestamped<LinkDescription>> e : descs.entrySet()) { | 476 | for (Entry<ProviderId, Timestamped<LinkDescription>> e : descs.entrySet()) { |
435 | - if (primaryProviderId.equals(e.getKey())) { | 477 | + if (baseProviderId.equals(e.getKey())) { |
436 | continue; | 478 | continue; |
437 | } | 479 | } |
438 | 480 | ||
... | @@ -445,10 +487,10 @@ public class GossipLinkStore | ... | @@ -445,10 +487,10 @@ public class GossipLinkStore |
445 | annotations = merge(annotations, e.getValue().value().annotations()); | 487 | annotations = merge(annotations, e.getValue().value().annotations()); |
446 | } | 488 | } |
447 | 489 | ||
448 | - return new DefaultLink(primaryProviderId , src, dst, type, annotations); | 490 | + return new DefaultLink(baseProviderId, src, dst, type, annotations); |
449 | } | 491 | } |
450 | 492 | ||
451 | - private Map<ProviderId, Timestamped<LinkDescription>> getLinkDescriptions(LinkKey key) { | 493 | + private Map<ProviderId, Timestamped<LinkDescription>> getOrCreateLinkDescriptions(LinkKey key) { |
452 | Map<ProviderId, Timestamped<LinkDescription>> r; | 494 | Map<ProviderId, Timestamped<LinkDescription>> r; |
453 | r = linkDescs.get(key); | 495 | r = linkDescs.get(key); |
454 | if (r != null) { | 496 | if (r != null) { |
... | @@ -464,11 +506,11 @@ public class GossipLinkStore | ... | @@ -464,11 +506,11 @@ public class GossipLinkStore |
464 | } | 506 | } |
465 | } | 507 | } |
466 | 508 | ||
467 | - private Timestamped<LinkDescription> getLinkDescription(LinkKey key, ProviderId providerId) { | ||
468 | - return getLinkDescriptions(key).get(providerId); | ||
469 | - } | ||
470 | - | ||
471 | private final Function<LinkKey, Link> lookupLink = new LookupLink(); | 509 | private final Function<LinkKey, Link> lookupLink = new LookupLink(); |
510 | + /** | ||
511 | + * Returns a Function to lookup Link instance using LinkKey from cache. | ||
512 | + * @return | ||
513 | + */ | ||
472 | private Function<LinkKey, Link> lookupLink() { | 514 | private Function<LinkKey, Link> lookupLink() { |
473 | return lookupLink; | 515 | return lookupLink; |
474 | } | 516 | } |
... | @@ -480,26 +522,12 @@ public class GossipLinkStore | ... | @@ -480,26 +522,12 @@ public class GossipLinkStore |
480 | } | 522 | } |
481 | } | 523 | } |
482 | 524 | ||
483 | - private static final class IsPrimary implements Predicate<Provided> { | ||
484 | - | ||
485 | - private static final Predicate<Provided> IS_PRIMARY = new IsPrimary(); | ||
486 | - public static final Predicate<Provided> isPrimary() { | ||
487 | - return IS_PRIMARY; | ||
488 | - } | ||
489 | - | ||
490 | - @Override | ||
491 | - public boolean apply(Provided input) { | ||
492 | - return !input.providerId().isAncillary(); | ||
493 | - } | ||
494 | - } | ||
495 | - | ||
496 | private void notifyDelegateIfNotNull(LinkEvent event) { | 525 | private void notifyDelegateIfNotNull(LinkEvent event) { |
497 | if (event != null) { | 526 | if (event != null) { |
498 | notifyDelegate(event); | 527 | notifyDelegate(event); |
499 | } | 528 | } |
500 | } | 529 | } |
501 | 530 | ||
502 | - // TODO: should we be throwing exception? | ||
503 | private void broadcastMessage(MessageSubject subject, Object event) throws IOException { | 531 | private void broadcastMessage(MessageSubject subject, Object event) throws IOException { |
504 | ClusterMessage message = new ClusterMessage( | 532 | ClusterMessage message = new ClusterMessage( |
505 | clusterService.getLocalNode().id(), | 533 | clusterService.getLocalNode().id(), |
... | @@ -508,17 +536,12 @@ public class GossipLinkStore | ... | @@ -508,17 +536,12 @@ public class GossipLinkStore |
508 | clusterCommunicator.broadcast(message); | 536 | clusterCommunicator.broadcast(message); |
509 | } | 537 | } |
510 | 538 | ||
511 | - // TODO: should we be throwing exception? | 539 | + private void unicastMessage(NodeId recipient, MessageSubject subject, Object event) throws IOException { |
512 | - private void unicastMessage(NodeId recipient, MessageSubject subject, Object event) { | 540 | + ClusterMessage message = new ClusterMessage( |
513 | - try { | 541 | + clusterService.getLocalNode().id(), |
514 | - ClusterMessage message = new ClusterMessage( | 542 | + subject, |
515 | - clusterService.getLocalNode().id(), | 543 | + SERIALIZER.encode(event)); |
516 | - subject, | 544 | + clusterCommunicator.unicast(message, recipient); |
517 | - SERIALIZER.encode(event)); | ||
518 | - clusterCommunicator.unicast(message, recipient); | ||
519 | - } catch (IOException e) { | ||
520 | - log.error("Failed to send a {} message to {}", subject.value(), recipient); | ||
521 | - } | ||
522 | } | 545 | } |
523 | 546 | ||
524 | private void notifyPeers(InternalLinkEvent event) throws IOException { | 547 | private void notifyPeers(InternalLinkEvent event) throws IOException { |
... | @@ -529,12 +552,22 @@ public class GossipLinkStore | ... | @@ -529,12 +552,22 @@ public class GossipLinkStore |
529 | broadcastMessage(GossipLinkStoreMessageSubjects.LINK_REMOVED, event); | 552 | broadcastMessage(GossipLinkStoreMessageSubjects.LINK_REMOVED, event); |
530 | } | 553 | } |
531 | 554 | ||
555 | + // notify peer, silently ignoring error | ||
532 | private void notifyPeer(NodeId peer, InternalLinkEvent event) { | 556 | private void notifyPeer(NodeId peer, InternalLinkEvent event) { |
533 | - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_UPDATE, event); | 557 | + try { |
558 | + unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_UPDATE, event); | ||
559 | + } catch (IOException e) { | ||
560 | + log.debug("Failed to notify peer {} with message {}", peer, event); | ||
561 | + } | ||
534 | } | 562 | } |
535 | 563 | ||
564 | + // notify peer, silently ignoring error | ||
536 | private void notifyPeer(NodeId peer, InternalLinkRemovedEvent event) { | 565 | private void notifyPeer(NodeId peer, InternalLinkRemovedEvent event) { |
537 | - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_REMOVED, event); | 566 | + try { |
567 | + unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_REMOVED, event); | ||
568 | + } catch (IOException e) { | ||
569 | + log.debug("Failed to notify peer {} with message {}", peer, event); | ||
570 | + } | ||
538 | } | 571 | } |
539 | 572 | ||
540 | private final class SendAdvertisementTask implements Runnable { | 573 | private final class SendAdvertisementTask implements Runnable { |
... | @@ -573,9 +606,9 @@ public class GossipLinkStore | ... | @@ -573,9 +606,9 @@ public class GossipLinkStore |
573 | } | 606 | } |
574 | 607 | ||
575 | try { | 608 | try { |
576 | - unicastMessage(peer, GossipLinkStoreMessageSubjects.LINK_ANTI_ENTROPY_ADVERTISEMENT, ad); | 609 | + unicastMessage(peer, LINK_ANTI_ENTROPY_ADVERTISEMENT, ad); |
577 | - } catch (Exception e) { | 610 | + } catch (IOException e) { |
578 | - log.error("Failed to send anti-entropy advertisement", e); | 611 | + log.debug("Failed to send anti-entropy advertisement to {}", peer); |
579 | return; | 612 | return; |
580 | } | 613 | } |
581 | } catch (Exception e) { | 614 | } catch (Exception e) { |
... | @@ -608,42 +641,75 @@ public class GossipLinkStore | ... | @@ -608,42 +641,75 @@ public class GossipLinkStore |
608 | return new LinkAntiEntropyAdvertisement(self, linkTimestamps, linkTombstones); | 641 | return new LinkAntiEntropyAdvertisement(self, linkTimestamps, linkTombstones); |
609 | } | 642 | } |
610 | 643 | ||
611 | - private void handleAntiEntropyAdvertisement(LinkAntiEntropyAdvertisement advertisement) { | 644 | + private void handleAntiEntropyAdvertisement(LinkAntiEntropyAdvertisement ad) { |
612 | - | ||
613 | - NodeId peer = advertisement.sender(); | ||
614 | 645 | ||
615 | - Map<LinkFragmentId, Timestamp> linkTimestamps = advertisement.linkTimestamps(); | 646 | + final NodeId sender = ad.sender(); |
616 | - Map<LinkKey, Timestamp> linkTombstones = advertisement.linkTombstones(); | 647 | + boolean localOutdated = false; |
617 | - for (Map.Entry<LinkFragmentId, Timestamp> entry : linkTimestamps.entrySet()) { | ||
618 | - LinkFragmentId linkFragmentId = entry.getKey(); | ||
619 | - Timestamp peerTimestamp = entry.getValue(); | ||
620 | 648 | ||
621 | - LinkKey key = linkFragmentId.linkKey(); | 649 | + for (Entry<LinkKey, Map<ProviderId, Timestamped<LinkDescription>>> |
622 | - ProviderId providerId = linkFragmentId.providerId(); | 650 | + l : linkDescs.entrySet()) { |
623 | - | 651 | + |
624 | - Timestamped<LinkDescription> linkDescription = getLinkDescription(key, providerId); | 652 | + final LinkKey key = l.getKey(); |
625 | - if (linkDescription.isNewer(peerTimestamp)) { | 653 | + final Map<ProviderId, Timestamped<LinkDescription>> link = l.getValue(); |
626 | - // I have more recent link description. update peer. | 654 | + synchronized (link) { |
627 | - notifyPeer(peer, new InternalLinkEvent(providerId, linkDescription)); | 655 | + Timestamp localLatest = removedLinks.get(key); |
628 | - } | 656 | + |
629 | - // else TODO: Peer has more recent link description. request it. | 657 | + for (Entry<ProviderId, Timestamped<LinkDescription>> p : link.entrySet()) { |
630 | - | 658 | + final ProviderId providerId = p.getKey(); |
631 | - Timestamp linkRemovedTimestamp = removedLinks.get(key); | 659 | + final Timestamped<LinkDescription> pDesc = p.getValue(); |
632 | - if (linkRemovedTimestamp != null && linkRemovedTimestamp.compareTo(peerTimestamp) > 0) { | 660 | + |
633 | - // peer has a zombie link. update peer. | 661 | + final LinkFragmentId fragId = new LinkFragmentId(key, providerId); |
634 | - notifyPeer(peer, new InternalLinkRemovedEvent(key, linkRemovedTimestamp)); | 662 | + // remote |
663 | + Timestamp remoteTimestamp = ad.linkTimestamps().get(fragId); | ||
664 | + if (remoteTimestamp == null) { | ||
665 | + remoteTimestamp = ad.linkTombstones().get(key); | ||
666 | + } | ||
667 | + if (remoteTimestamp == null || | ||
668 | + pDesc.isNewer(remoteTimestamp)) { | ||
669 | + // I have more recent link description. update peer. | ||
670 | + notifyPeer(sender, new InternalLinkEvent(providerId, pDesc)); | ||
671 | + } else { | ||
672 | + final Timestamp remoteLive = ad.linkTimestamps().get(fragId); | ||
673 | + if (remoteLive != null && | ||
674 | + remoteLive.compareTo(pDesc.timestamp()) > 0) { | ||
675 | + // I have something outdated | ||
676 | + localOutdated = true; | ||
677 | + } | ||
678 | + } | ||
679 | + | ||
680 | + // search local latest along the way | ||
681 | + if (localLatest == null || | ||
682 | + pDesc.isNewer(localLatest)) { | ||
683 | + localLatest = pDesc.timestamp(); | ||
684 | + } | ||
685 | + } | ||
686 | + // Tests if remote remove is more recent then local latest. | ||
687 | + final Timestamp remoteRemove = ad.linkTombstones().get(key); | ||
688 | + if (remoteRemove != null) { | ||
689 | + if (localLatest != null && | ||
690 | + localLatest.compareTo(remoteRemove) < 0) { | ||
691 | + // remote remove is more recent | ||
692 | + notifyDelegateIfNotNull(removeLinkInternal(key, remoteRemove)); | ||
693 | + } | ||
694 | + } | ||
635 | } | 695 | } |
636 | } | 696 | } |
637 | 697 | ||
638 | - for (Map.Entry<LinkKey, Timestamp> entry : linkTombstones.entrySet()) { | 698 | + // populate remove info if not known locally |
639 | - LinkKey key = entry.getKey(); | 699 | + for (Entry<LinkKey, Timestamp> remoteRm : ad.linkTombstones().entrySet()) { |
640 | - Timestamp peerTimestamp = entry.getValue(); | 700 | + final LinkKey key = remoteRm.getKey(); |
701 | + final Timestamp remoteRemove = remoteRm.getValue(); | ||
702 | + // relying on removeLinkInternal to ignore stale info | ||
703 | + notifyDelegateIfNotNull(removeLinkInternal(key, remoteRemove)); | ||
704 | + } | ||
641 | 705 | ||
642 | - ProviderId primaryProviderId = pickPrimaryProviderId(getLinkDescriptions(key)); | 706 | + if (localOutdated) { |
643 | - if (primaryProviderId != null) { | 707 | + // send back advertisement to speed up convergence |
644 | - if (!getLinkDescription(key, primaryProviderId).isNewer(peerTimestamp)) { | 708 | + try { |
645 | - notifyDelegateIfNotNull(removeLinkInternal(key, peerTimestamp)); | 709 | + unicastMessage(sender, LINK_ANTI_ENTROPY_ADVERTISEMENT, |
646 | - } | 710 | + createAdvertisement()); |
711 | + } catch (IOException e) { | ||
712 | + log.debug("Failed to send back active advertisement"); | ||
647 | } | 713 | } |
648 | } | 714 | } |
649 | } | 715 | } | ... | ... |
-
Please register or login to post a comment