[NO ISSUE] Metadata partition bootstrap, replication

- Send metadata partition id as part of metadata bootstrap
- Ignore requests to add replicas to myself
- Avoid hard checkpoint at shutdown

Change-Id: I08c91de570bbc90f5532329f72db21a424993fed
Reviewed-on: https://asterix-gerrit.ics.uci.edu/2369
Reviewed-by: Murtadha Hubail <mhubail@apache.org>
Integration-Tests: Murtadha Hubail <mhubail@apache.org>
Tested-by: Murtadha Hubail <mhubail@apache.org>
Contrib: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/NCAppRuntimeContext.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/NCAppRuntimeContext.java
index 1de6938..d42db39 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/NCAppRuntimeContext.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/NCAppRuntimeContext.java
@@ -416,10 +416,10 @@
     }
 
     @Override
-    public void initializeMetadata(boolean newUniverse) throws Exception {
+    public void initializeMetadata(boolean newUniverse, int partitionId) throws Exception {
         LOGGER.info("Bootstrapping metadata");
         MetadataNode.INSTANCE.initialize(this, ncExtensionManager.getMetadataTupleTranslatorProvider(),
-                ncExtensionManager.getMetadataExtensions());
+                ncExtensionManager.getMetadataExtensions(), partitionId);
 
         //noinspection unchecked
         ConcurrentHashMap<CcId, IAsterixStateProxy> proxyMap =
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/RecoveryManager.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/RecoveryManager.java
index 3717673..de3c691 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/RecoveryManager.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/RecoveryManager.java
@@ -668,8 +668,6 @@
 
     @Override
     public void stop(boolean dumpState, OutputStream os) throws IOException {
-        // Shutdown checkpoint
-        checkpointManager.doSharpCheckpoint();
     }
 
     @Override
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/ReplicaManager.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/ReplicaManager.java
index 8c733fb..30416a3 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/ReplicaManager.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/ReplicaManager.java
@@ -18,6 +18,7 @@
  */
 package org.apache.asterix.app.nc;
 
+import java.net.InetSocketAddress;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -35,10 +36,15 @@
 import org.apache.asterix.common.transactions.IRecoveryManager;
 import org.apache.asterix.replication.api.PartitionReplica;
 import org.apache.asterix.transaction.management.resource.PersistentLocalResourceRepository;
+import org.apache.hyracks.api.config.IApplicationConfig;
 import org.apache.hyracks.api.exceptions.HyracksDataException;
+import org.apache.hyracks.control.common.controllers.NCConfig;
 import org.apache.hyracks.storage.common.LocalResource;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 public class ReplicaManager implements IReplicaManager {
+    private static final Logger LOGGER = LogManager.getLogger();
 
     private final INcApplicationContext appCtx;
     /**
@@ -61,6 +67,10 @@
             throw new IllegalStateException(
                     "This node is not the current master of partition(" + id.getPartition() + ")");
         }
+        if (isSelf(id)) {
+            LOGGER.info("ignoring request to add replica to ourselves");
+            return;
+        }
         replicas.computeIfAbsent(id, k -> new PartitionReplica(k, appCtx));
         replicas.get(id).sync();
     }
@@ -88,6 +98,9 @@
 
     @Override
     public synchronized void promote(int partition) throws HyracksDataException {
+        if (partitions.contains(partition)) {
+            return;
+        }
         final PersistentLocalResourceRepository localResourceRepository =
                 (PersistentLocalResourceRepository) appCtx.getLocalResourceRepository();
         localResourceRepository.cleanup(partition);
@@ -120,4 +133,13 @@
             datasetLifecycleManager.close(resource.getPath());
         }
     }
+
+    private boolean isSelf(ReplicaIdentifier id) {
+        IApplicationConfig appConfig = appCtx.getServiceContext().getAppConfig();
+        String host = appConfig.getString(NCConfig.Option.REPLICATION_LISTEN_ADDRESS);
+        int port = appConfig.getInt(NCConfig.Option.REPLICATION_LISTEN_PORT);
+
+        final InetSocketAddress replicaAddress = new InetSocketAddress(host, port);
+        return id.equals(ReplicaIdentifier.of(id.getPartition(), replicaAddress));
+    }
 }
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/task/MetadataBootstrapTask.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/task/MetadataBootstrapTask.java
index 001af23..c18b967 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/task/MetadataBootstrapTask.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/nc/task/MetadataBootstrapTask.java
@@ -28,13 +28,19 @@
 public class MetadataBootstrapTask implements INCLifecycleTask {
 
     private static final long serialVersionUID = 1L;
+    private final int partitionId;
+
+    public MetadataBootstrapTask(int partitionId) {
+        this.partitionId = partitionId;
+    }
 
     @Override
     public void perform(CcId ccId, IControllerService cs) throws HyracksDataException {
         INcApplicationContext appContext = (INcApplicationContext) cs.getApplicationContext();
         try {
+            appContext.getReplicaManager().promote(partitionId);
             SystemState state = appContext.getTransactionSubsystem().getRecoveryManager().getSystemState();
-            appContext.initializeMetadata(state == SystemState.PERMANENT_DATA_LOSS);
+            appContext.initializeMetadata(state == SystemState.PERMANENT_DATA_LOSS, partitionId);
         } catch (Exception e) {
             throw HyracksDataException.create(e);
         }
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/NcLifecycleCoordinator.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/NcLifecycleCoordinator.java
index 8939059..25e768d 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/NcLifecycleCoordinator.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/NcLifecycleCoordinator.java
@@ -155,7 +155,7 @@
             tasks.add(new StartReplicationServiceTask());
         }
         if (isMetadataNode) {
-            tasks.add(new MetadataBootstrapTask());
+            tasks.add(new MetadataBootstrapTask(clusterManager.getMetadataPartition().getPartitionId()));
         }
         tasks.add(new ExternalLibrarySetupTask(isMetadataNode));
         tasks.add(new CheckpointTask());
@@ -184,7 +184,8 @@
         }
         // if current metadata node is active, we need to unbind its metadata proxy objects
         if (clusterManager.isMetadataNodeActive()) {
-            MetadataNodeRequestMessage msg = new MetadataNodeRequestMessage(false);
+            MetadataNodeRequestMessage msg =
+                    new MetadataNodeRequestMessage(false, clusterManager.getMetadataPartition().getPartitionId());
             try {
                 messageBroker.sendApplicationMessageToNC(msg, metadataNodeId);
                 // when the current node responses, we will bind to the new one
@@ -207,7 +208,8 @@
     }
 
     private void requestMetadataNodeTakeover(String node) throws HyracksDataException {
-        MetadataNodeRequestMessage msg = new MetadataNodeRequestMessage(true);
+        MetadataNodeRequestMessage msg =
+                new MetadataNodeRequestMessage(true, clusterManager.getMetadataPartition().getPartitionId());
         try {
             messageBroker.sendApplicationMessageToNC(msg, node);
         } catch (Exception e) {
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/message/MetadataNodeRequestMessage.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/message/MetadataNodeRequestMessage.java
index 4443825..817bbe6 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/message/MetadataNodeRequestMessage.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/app/replication/message/MetadataNodeRequestMessage.java
@@ -34,9 +34,11 @@
     private static final long serialVersionUID = 1L;
     private static final Logger LOGGER = LogManager.getLogger();
     private final boolean export;
+    private final int partitionId;
 
-    public MetadataNodeRequestMessage(boolean export) {
+    public MetadataNodeRequestMessage(boolean export, int partitionId) {
         this.export = export;
+        this.partitionId = partitionId;
     }
 
     @Override
@@ -45,7 +47,7 @@
         HyracksDataException hde = null;
         try {
             if (export) {
-                appContext.initializeMetadata(false);
+                appContext.initializeMetadata(false, partitionId);
                 appContext.exportMetadataNodeStub();
                 appContext.bindMetadataNodeStub(getCcId());
             } else {
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/INcApplicationContext.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/INcApplicationContext.java
index a02bda5..82936b3 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/INcApplicationContext.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/api/INcApplicationContext.java
@@ -94,9 +94,10 @@
      * Initializes the metadata node and bootstraps the metadata.
      *
      * @param newUniverse
+     * @param partitionId
      * @throws Exception
      */
-    void initializeMetadata(boolean newUniverse) throws Exception;
+    void initializeMetadata(boolean newUniverse, int partitionId) throws Exception;
 
     /**
      * Unexports the metadata node from the RMI registry
diff --git a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/MetadataProperties.java b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/MetadataProperties.java
index 948bdad..0b18f98 100644
--- a/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/MetadataProperties.java
+++ b/asterixdb/asterix-common/src/main/java/org/apache/asterix/common/config/MetadataProperties.java
@@ -105,11 +105,6 @@
         return accessor.getString(Option.METADATA_NODE);
     }
 
-    public ClusterPartition getMetadataPartition() {
-        // metadata partition is always the first partition on the metadata node
-        return accessor.getNodePartitions().get(getMetadataNodeName())[0];
-    }
-
     public Map<String, String[]> getStores() {
         return accessor.getStores();
     }
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java
index 72d5cf5..fd21941 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/MetadataNode.java
@@ -145,11 +145,12 @@
     }
 
     public void initialize(INcApplicationContext runtimeContext,
-            MetadataTupleTranslatorProvider tupleTranslatorProvider, List<IMetadataExtension> metadataExtensions) {
+            MetadataTupleTranslatorProvider tupleTranslatorProvider, List<IMetadataExtension> metadataExtensions,
+            int partitionId) {
         this.tupleTranslatorProvider = tupleTranslatorProvider;
         this.transactionSubsystem = runtimeContext.getTransactionSubsystem();
         this.datasetLifecycleManager = runtimeContext.getDatasetLifecycleManager();
-        this.metadataStoragePartition = runtimeContext.getMetadataProperties().getMetadataPartition().getPartitionId();
+        this.metadataStoragePartition = partitionId;
         if (metadataExtensions != null) {
             extensionDatasets = new HashMap<>();
             for (IMetadataExtension metadataExtension : metadataExtensions) {
@@ -161,6 +162,10 @@
         this.txnIdFactory = new BulkTxnIdFactory();
     }
 
+    public int getMetadataStoragePartition() {
+        return metadataStoragePartition;
+    }
+
     @Override
     public void ensureMinimumTxnId(long maxId) throws ACIDException, RemoteException {
         txnIdFactory.ensureMinimumId(maxId);
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataBootstrap.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataBootstrap.java
index 9ebd21b..23806bd 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataBootstrap.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/bootstrap/MetadataBootstrap.java
@@ -25,7 +25,6 @@
 import java.util.List;
 
 import org.apache.asterix.common.api.INcApplicationContext;
-import org.apache.asterix.common.cluster.ClusterPartition;
 import org.apache.asterix.common.config.DatasetConfig.DatasetType;
 import org.apache.asterix.common.config.GlobalConfig;
 import org.apache.asterix.common.config.MetadataProperties;
@@ -44,6 +43,7 @@
 import org.apache.asterix.external.indexing.ExternalFile;
 import org.apache.asterix.metadata.IDatasetDetails;
 import org.apache.asterix.metadata.MetadataManager;
+import org.apache.asterix.metadata.MetadataNode;
 import org.apache.asterix.metadata.MetadataTransactionContext;
 import org.apache.asterix.metadata.api.IMetadataIndex;
 import org.apache.asterix.metadata.entities.BuiltinTypeMap;
@@ -311,11 +311,10 @@
         if (!appContext.getDatasetMemoryManager().reserve(index.getDatasetId().getId())) {
             throw new IllegalStateException("Failed to reserve memory for metadata dataset (" + datasetId + ")");
         }
-        ClusterPartition metadataPartition = appContext.getMetadataProperties().getMetadataPartition();
-        int metadataDeviceId = metadataPartition.getIODeviceNum();
-        String metadataPartitionPath = StoragePathUtil.prepareStoragePartitionPath(metadataPartition.getPartitionId());
+        String metadataPartitionPath =
+                StoragePathUtil.prepareStoragePartitionPath(MetadataNode.INSTANCE.getMetadataStoragePartition());
         String resourceName = metadataPartitionPath + File.separator + index.getFileNameRelativePath();
-        FileReference file = ioManager.getFileReference(metadataDeviceId, resourceName);
+        FileReference file = ioManager.resolve(resourceName);
         index.setFile(file);
         ITypeTraits[] typeTraits = index.getTypeTraits();
         IBinaryComparatorFactory[] cmpFactories = index.getKeyBinaryComparatorFactory();