BUG-6937: Add ReachableMember case to Gossiper
[controller.git] / opendaylight / md-sal / sal-remoterpc-connector / src / main / java / org / opendaylight / controller / remote / rpc / registry / gossip / BucketStore.java
index b50dfb1ba3e196f66e33d74438d43a2aca2d81ee..81e6a9ccc3c253e0e409e37e75f8d7ad1a4af888 100644 (file)
@@ -13,59 +13,56 @@ import akka.actor.ActorRefProvider;
 import akka.actor.Address;
 import akka.actor.Props;
 import akka.cluster.ClusterActorRefProvider;
-import akka.event.Logging;
-import akka.event.LoggingAdapter;
-import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActorWithMetering;
-import org.opendaylight.controller.remote.rpc.RemoteRpcProviderConfig;
-import org.opendaylight.controller.utils.ConditionalProbe;
-
+import com.google.common.base.Preconditions;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBuckets;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBucketsReply;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketVersions;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketVersionsReply;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketsByMembers;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketsByMembersReply;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetLocalBucket;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetLocalBucketReply;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.UpdateBucket;
-import static org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.UpdateRemoteBuckets;
+import org.opendaylight.controller.cluster.common.actor.AbstractUntypedActorWithMetering;
+import org.opendaylight.controller.remote.rpc.RemoteRpcProviderConfig;
+import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBuckets;
+import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetAllBucketsReply;
+import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketVersions;
+import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketVersionsReply;
+import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketsByMembers;
+import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.GetBucketsByMembersReply;
+import org.opendaylight.controller.remote.rpc.registry.gossip.Messages.BucketStoreMessages.UpdateRemoteBuckets;
+import org.opendaylight.controller.utils.ConditionalProbe;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * A store that syncs its data across nodes in the cluster.
  * It maintains a {@link org.opendaylight.controller.remote.rpc.registry.gossip.Bucket} per node. Buckets are versioned.
  * A node can write ONLY to its bucket. This way, write conflicts are avoided.
+ *
  * <p>
- * Buckets are sync'ed across nodes using Gossip protocol (http://en.wikipedia.org/wiki/Gossip_protocol)<p>
+ * Buckets are sync'ed across nodes using Gossip protocol (http://en.wikipedia.org/wiki/Gossip_protocol).
  * This store uses a {@link org.opendaylight.controller.remote.rpc.registry.gossip.Gossiper}.
  *
  */
-public class BucketStore extends AbstractUntypedActorWithMetering {
+public class BucketStore<T extends Copier<T>> extends AbstractUntypedActorWithMetering {
+
+    private static final Long NO_VERSION = -1L;
 
-    final LoggingAdapter log = Logging.getLogger(getContext().system(), this);
+    protected final Logger log = LoggerFactory.getLogger(getClass());
 
     /**
-     * Bucket owned by the node
+     * Bucket owned by the node.
      */
-    private BucketImpl localBucket = new BucketImpl();
+    private final BucketImpl<T> localBucket = new BucketImpl<>();
 
     /**
-     * Buckets ownded by other known nodes in the cluster
+     * Buckets ownded by other known nodes in the cluster.
      */
-    private ConcurrentMap<Address, Bucket> remoteBuckets = new ConcurrentHashMap<>();
+    private final Map<Address, Bucket<T>> remoteBuckets = new HashMap<>();
 
     /**
-     * Bucket version for every known node in the cluster including this node
+     * Bucket version for every known node in the cluster including this node.
      */
-    private ConcurrentMap<Address, Long> versions = new ConcurrentHashMap<>();
+    private final Map<Address, Long> versions = new HashMap<>();
 
     /**
-     * Cluster address for this node
+     * Cluster address for this node.
      */
     private Address selfAddress;
 
@@ -73,20 +70,21 @@ public class BucketStore extends AbstractUntypedActorWithMetering {
 
     private final RemoteRpcProviderConfig config;
 
-    public BucketStore(){
-        config = new RemoteRpcProviderConfig(getContext().system().settings().config());
+    public BucketStore(RemoteRpcProviderConfig config) {
+        this.config = Preconditions.checkNotNull(config);
     }
 
     @Override
-    public void preStart(){
+    public void preStart() {
         ActorRefProvider provider = getContext().provider();
         selfAddress = provider.getDefaultAddress();
 
-        if ( provider instanceof ClusterActorRefProvider)
-            getContext().actorOf(Props.create(Gossiper.class).withMailbox(config.getMailBoxName()), "gossiper");
+        if ( provider instanceof ClusterActorRefProvider) {
+            getContext().actorOf(Props.create(Gossiper.class, config).withMailbox(config.getMailBoxName()), "gossiper");
+        }
     }
 
-
+    @SuppressWarnings("unchecked")
     @Override
     protected void handleReceive(Object message) throws Exception {
         if (probe != null) {
@@ -94,68 +92,47 @@ public class BucketStore extends AbstractUntypedActorWithMetering {
         }
 
         if (message instanceof ConditionalProbe) {
+            // The ConditionalProbe is only used for unit tests.
             log.info("Received probe {} {}", getSelf(), message);
             probe = (ConditionalProbe) message;
-        } else if (message instanceof UpdateBucket) {
-            receiveUpdateBucket(((UpdateBucket) message).getBucket());
+            // Send back any message to tell the caller we got the probe.
+            getSender().tell("Got it", getSelf());
         } else if (message instanceof GetAllBuckets) {
-            receiveGetAllBucket();
-        } else if (message instanceof GetLocalBucket) {
-            receiveGetLocalBucket();
+            receiveGetAllBuckets();
         } else if (message instanceof GetBucketsByMembers) {
-            receiveGetBucketsByMembers(
-                    ((GetBucketsByMembers) message).getMembers());
+            receiveGetBucketsByMembers(((GetBucketsByMembers) message).getMembers());
         } else if (message instanceof GetBucketVersions) {
             receiveGetBucketVersions();
         } else if (message instanceof UpdateRemoteBuckets) {
-            receiveUpdateRemoteBuckets(
-                    ((UpdateRemoteBuckets) message).getBuckets());
+            receiveUpdateRemoteBuckets(((UpdateRemoteBuckets<T>) message).getBuckets());
         } else {
-            if(log.isDebugEnabled()) {
-                log.debug("Unhandled message [{}]", message);
-            }
+            log.debug("Unhandled message [{}]", message);
             unhandled(message);
         }
     }
 
-    /**
-     * Returns a copy of bucket owned by this node
-     */
-    private void receiveGetLocalBucket() {
-        final ActorRef sender = getSender();
-        GetLocalBucketReply reply = new GetLocalBucketReply(localBucket);
-        sender.tell(reply, getSelf());
+    protected RemoteRpcProviderConfig getConfig() {
+        return config;
     }
 
     /**
-     * Updates the bucket owned by this node
-     *
-     * @param updatedBucket
+     * Returns all the buckets the this node knows about, self owned + remote.
      */
-    void receiveUpdateBucket(Bucket updatedBucket){
-
-        localBucket = (BucketImpl) updatedBucket;
-        versions.put(selfAddress, localBucket.getVersion());
-    }
-
-    /**
-     * Returns all the buckets the this node knows about, self owned + remote
-     */
-    void receiveGetAllBucket(){
+    void receiveGetAllBuckets() {
         final ActorRef sender = getSender();
-        sender.tell(new GetAllBucketsReply(getAllBuckets()), getSelf());
+        sender.tell(new GetAllBucketsReply<>(getAllBuckets()), getSelf());
     }
 
     /**
-     * Helper to collect all known buckets
+     * Helper to collect all known buckets.
      *
      * @return self owned + remote buckets
      */
-    Map<Address, Bucket> getAllBuckets(){
-        Map<Address, Bucket> all = new HashMap<>(remoteBuckets.size() + 1);
+    Map<Address, Bucket<T>> getAllBuckets() {
+        Map<Address, Bucket<T>> all = new HashMap<>(remoteBuckets.size() + 1);
 
         //first add the local bucket
-        all.put(selfAddress, localBucket);
+        all.put(selfAddress, new BucketImpl<>(localBucket));
 
         //then get all remote buckets
         all.putAll(remoteBuckets);
@@ -164,73 +141,81 @@ public class BucketStore extends AbstractUntypedActorWithMetering {
     }
 
     /**
-     * Returns buckets for requested members that this node knows about
+     * Returns buckets for requested members that this node knows about.
      *
      * @param members requested members
      */
-    void receiveGetBucketsByMembers(Set<Address> members){
+    void receiveGetBucketsByMembers(Set<Address> members) {
         final ActorRef sender = getSender();
-        Map<Address, Bucket> buckets = getBucketsByMembers(members);
-        sender.tell(new GetBucketsByMembersReply(buckets), getSelf());
+        Map<Address, Bucket<T>> buckets = getBucketsByMembers(members);
+        sender.tell(new GetBucketsByMembersReply<>(buckets), getSelf());
     }
 
     /**
-     * Helper to collect buckets for requested memebers
+     * Helper to collect buckets for requested members.
      *
      * @param members requested members
-     * @return buckets for requested memebers
+     * @return buckets for requested members
      */
-    Map<Address, Bucket> getBucketsByMembers(Set<Address> members) {
-        Map<Address, Bucket> buckets = new HashMap<>();
+    Map<Address, Bucket<T>> getBucketsByMembers(Set<Address> members) {
+        Map<Address, Bucket<T>> buckets = new HashMap<>();
 
         //first add the local bucket if asked
-        if (members.contains(selfAddress))
-            buckets.put(selfAddress, localBucket);
+        if (members.contains(selfAddress)) {
+            buckets.put(selfAddress, new BucketImpl<>(localBucket));
+        }
 
         //then get buckets for requested remote nodes
-        for (Address address : members){
-            if (remoteBuckets.containsKey(address))
+        for (Address address : members) {
+            if (remoteBuckets.containsKey(address)) {
                 buckets.put(address, remoteBuckets.get(address));
+            }
         }
 
         return buckets;
     }
 
     /**
-     * Returns versions for all buckets known
+     * Returns versions for all buckets known.
      */
-    void receiveGetBucketVersions(){
+    void receiveGetBucketVersions() {
         final ActorRef sender = getSender();
         GetBucketVersionsReply reply = new GetBucketVersionsReply(versions);
         sender.tell(reply, getSelf());
     }
 
     /**
-     * Update local copy of remote buckets where local copy's version is older
+     * Update local copy of remote buckets where local copy's version is older.
      *
      * @param receivedBuckets buckets sent by remote
      *                        {@link org.opendaylight.controller.remote.rpc.registry.gossip.Gossiper}
      */
-    void receiveUpdateRemoteBuckets(Map<Address, Bucket> receivedBuckets){
-
-        if (receivedBuckets == null || receivedBuckets.isEmpty())
+    void receiveUpdateRemoteBuckets(Map<Address, Bucket<T>> receivedBuckets) {
+        log.debug("{}: receiveUpdateRemoteBuckets: {}", selfAddress, receivedBuckets);
+        if (receivedBuckets == null || receivedBuckets.isEmpty()) {
             return; //nothing to do
+        }
 
         //Remote cant update self's bucket
         receivedBuckets.remove(selfAddress);
 
-        for (Map.Entry<Address, Bucket> entry : receivedBuckets.entrySet()){
+        for (Map.Entry<Address, Bucket<T>> entry : receivedBuckets.entrySet()) {
 
             Long localVersion = versions.get(entry.getKey());
-            if (localVersion == null) localVersion = -1L;
+            if (localVersion == null) {
+                localVersion = NO_VERSION;
+            }
 
-            Bucket receivedBucket = entry.getValue();
+            Bucket<T> receivedBucket = entry.getValue();
 
-            if (receivedBucket == null)
+            if (receivedBucket == null) {
                 continue;
+            }
 
             Long remoteVersion = receivedBucket.getVersion();
-            if (remoteVersion == null) remoteVersion = -1L;
+            if (remoteVersion == null) {
+                remoteVersion = NO_VERSION;
+            }
 
             //update only if remote version is newer
             if ( remoteVersion.longValue() > localVersion.longValue() ) {
@@ -238,40 +223,29 @@ public class BucketStore extends AbstractUntypedActorWithMetering {
                 versions.put(entry.getKey(), remoteVersion);
             }
         }
-        if(log.isDebugEnabled()) {
-            log.debug("State after update - Local Bucket [{}], Remote Buckets [{}]", localBucket, remoteBuckets);
-        }
-    }
 
-    ///
-    ///Getter Setters
-    ///
+        log.debug("State after update - Local Bucket [{}], Remote Buckets [{}]", localBucket, remoteBuckets);
 
-    BucketImpl getLocalBucket() {
-        return localBucket;
+        onBucketsUpdated();
     }
 
-    void setLocalBucket(BucketImpl localBucket) {
-        this.localBucket = localBucket;
-    }
-
-    ConcurrentMap<Address, Bucket> getRemoteBuckets() {
-        return remoteBuckets;
+    protected void onBucketsUpdated() {
     }
 
-    void setRemoteBuckets(ConcurrentMap<Address, Bucket> remoteBuckets) {
-        this.remoteBuckets = remoteBuckets;
+    public BucketImpl<T> getLocalBucket() {
+        return localBucket;
     }
 
-    ConcurrentMap<Address, Long> getVersions() {
-        return versions;
+    protected void updateLocalBucket(T data) {
+        localBucket.setData(data);
+        versions.put(selfAddress, localBucket.getVersion());
     }
 
-    void setVersions(ConcurrentMap<Address, Long> versions) {
-        this.versions = versions;
+    public Map<Address, Bucket<T>> getRemoteBuckets() {
+        return remoteBuckets;
     }
 
-    Address getSelfAddress() {
-        return selfAddress;
+    public Map<Address, Long> getVersions() {
+        return versions;
     }
 }