[ASTERIXDB-3580][COMP] Ensure the computation locations are sorted

- user model changes: no
- storage format changes: no
- interface changes: no

Details:
The cluster locations used by datasets are on sorted nodes.
The computation locations should also be made sorted.

Ext-ref: MB-63354, MB-65314
Change-Id: Id7463f54455ce1e5f874b75399c1ec9b96250b5f
Reviewed-on: https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/19543
Integration-Tests: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Tested-by: Jenkins <jenkins@fulliautomatix.ics.uci.edu>
Reviewed-by: Ali Alsuliman <ali.al.solaiman@gmail.com>
Reviewed-by: Ian Maxon <imaxon@apache.org>
diff --git a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
index c5fc395..b30a9009 100644
--- a/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
+++ b/asterixdb/asterix-app/src/main/java/org/apache/asterix/api/common/APIFramework.java
@@ -535,7 +535,9 @@
                 locations.add(nodeId);
             }
         });
-        return new AlgebricksAbsolutePartitionConstraint(locations.toArray(new String[0]));
+        String[] sortedLocations = locations.toArray(new String[0]);
+        Arrays.sort(sortedLocations);
+        return new AlgebricksAbsolutePartitionConstraint(sortedLocations);
     }
 
     // Gets the total number of available cores in the cluster.
diff --git a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
index 6ed0af9..66c184b 100644
--- a/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
+++ b/asterixdb/asterix-metadata/src/main/java/org/apache/asterix/metadata/declared/MetadataProvider.java
@@ -1881,7 +1881,7 @@
         if (!(nodeDomain instanceof DefaultNodeGroupDomain inputDomain)) {
             return null;
         }
-        String[] inputLocations = inputDomain.getNodes();
+        String[] inputLocations = inputDomain.getSortedNodes();
         AlgebricksAbsolutePartitionConstraint locations = dataPartitioningProvider.getClusterLocations();
         String[] clusterLocations = locations.getLocations();
         if (!Arrays.equals(inputLocations, clusterLocations)) {
diff --git a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/properties/DefaultNodeGroupDomain.java b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/properties/DefaultNodeGroupDomain.java
index a0ef64e..5921eea 100644
--- a/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/properties/DefaultNodeGroupDomain.java
+++ b/hyracks-fullstack/algebricks/algebricks-core/src/main/java/org/apache/hyracks/algebricks/core/algebra/properties/DefaultNodeGroupDomain.java
@@ -18,6 +18,7 @@
  */
 package org.apache.hyracks.algebricks.core.algebra.properties;
 
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.commons.collections4.MultiSet;
@@ -28,16 +29,12 @@
 
 public class DefaultNodeGroupDomain implements INodeDomain {
 
-    private MultiSet<String> nodes = new HashMultiSet<>();
+    private final MultiSet<String> nodes = new HashMultiSet<>();
 
     public DefaultNodeGroupDomain(List<String> nodes) {
         this.nodes.addAll(nodes);
     }
 
-    public DefaultNodeGroupDomain(DefaultNodeGroupDomain domain) {
-        this.nodes.addAll(domain.nodes);
-    }
-
     public DefaultNodeGroupDomain(AlgebricksPartitionConstraint clusterLocations) {
         if (clusterLocations.getPartitionConstraintType() == PartitionConstraintType.ABSOLUTE) {
             AlgebricksAbsolutePartitionConstraint absPc = (AlgebricksAbsolutePartitionConstraint) clusterLocations;
@@ -52,10 +49,10 @@
 
     @Override
     public boolean sameAs(INodeDomain domain) {
-        if (!(domain instanceof DefaultNodeGroupDomain)) {
+        if (!(domain instanceof DefaultNodeGroupDomain nodeDomain)) {
             return false;
         }
-        DefaultNodeGroupDomain nodeDomain = (DefaultNodeGroupDomain) domain;
+        // TODO(ali): this should be revisited. it does not check for order of the nodes.
         return nodes.equals(nodeDomain.nodes);
     }
 
@@ -69,7 +66,17 @@
         return nodes.size();
     }
 
+    /**
+     * Returns the nodes in the domain. The order of the nodes is arbitrary on each invocation.
+     * @return returns the nodes in the domain.
+     */
     public String[] getNodes() {
         return nodes.toArray(new String[0]);
     }
+
+    public String[] getSortedNodes() {
+        String[] sortedNodes = nodes.toArray(new String[0]);
+        Arrays.sort(sortedNodes);
+        return sortedNodes;
+    }
 }