/**
   * Processes anti-entropy ad from peer by taking following actions: 1. If peer has an old entry,
   * updates peer. 2. If peer indicates an entry is removed and has a more recent timestamp than the
   * local entry, update local state.
   */
  private List<EventuallyConsistentMapEvent<K, V>> antiEntropyCheckLocalItems(
      AntiEntropyAdvertisement<K> ad) {
    final List<EventuallyConsistentMapEvent<K, V>> externalEvents = Lists.newLinkedList();
    final NodeId sender = ad.sender();
    final List<NodeId> peers = ImmutableList.of(sender);
    Set<K> staleOrMissing = new HashSet<>();
    Set<K> locallyUnknown = new HashSet<>(ad.digest().keySet());

    items.forEach(
        (key, localValue) -> {
          locallyUnknown.remove(key);
          MapValue.Digest remoteValueDigest = ad.digest().get(key);
          if (remoteValueDigest == null || localValue.isNewerThan(remoteValueDigest.timestamp())) {
            // local value is more recent, push to sender
            queueUpdate(new UpdateEntry<>(key, localValue), peers);
          } else if (remoteValueDigest != null
              && remoteValueDigest.isNewerThan(localValue.digest())
              && remoteValueDigest.isTombstone()) {
            // remote value is more recent and a tombstone: update local value
            MapValue<V> tombstone = MapValue.tombstone(remoteValueDigest.timestamp());
            MapValue<V> previousValue =
                removeInternal(key, Optional.empty(), Optional.of(tombstone));
            if (previousValue != null && previousValue.isAlive()) {
              externalEvents.add(
                  new EventuallyConsistentMapEvent<>(mapName, REMOVE, key, previousValue.get()));
            }
          } else if (remoteValueDigest.isNewerThan(localValue.digest())) {
            // Not a tombstone and remote is newer
            staleOrMissing.add(key);
          }
        });
    // Keys missing in local map
    staleOrMissing.addAll(locallyUnknown);
    // Request updates that we missed out on
    sendUpdateRequestToPeer(sender, staleOrMissing);
    return externalEvents;
  }
 private AntiEntropyResponse handleAntiEntropyAdvertisement(AntiEntropyAdvertisement<K> ad) {
   if (destroyed || underHighLoad()) {
     return AntiEntropyResponse.IGNORED;
   }
   try {
     if (log.isTraceEnabled()) {
       log.trace(
           "Received anti-entropy advertisement from {} for {} with {} entries in it",
           ad.sender(),
           mapName,
           ad.digest().size());
     }
     antiEntropyCheckLocalItems(ad).forEach(this::notifyListeners);
   } catch (Exception e) {
     log.warn("Error handling anti-entropy advertisement", e);
     return AntiEntropyResponse.FAILED;
   }
   return AntiEntropyResponse.PROCESSED;
 }