View Javadoc

1   package org.apache.helix.tools;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Random;
27  import java.util.TreeMap;
28  
29  import org.apache.helix.ZNRecord;
30  import org.apache.helix.model.IdealState.IdealStateProperty;
31  
32  
33  /*
34   * Ideal state calculator for the cluster manager V1. The ideal state is
35   * calculated by randomly assign master partitions to storage nodes.
36   *
37   * Note that the following code is a native strategy and is for cluster manager V1 only. We will
38   * use the other algorithm to calculate the ideal state in future milestones.
39   *
40   *
41   * */
42  
43  public class IdealStateCalculatorByShuffling
44  {
45    /*
46     * Given the number of nodes, partitions and replica number, calculate the
47     * ideal state in the following manner: For the master partition assignment,
48     * 1. construct Arraylist partitionList, with partitionList[i] = i; 2. Shuffle
49     * the partitions array 3. Scan the shuffled array, then assign
50     * partitionList[i] to node (i % nodes)
51     *
52     * for the slave partitions, simply put them in the node after the node that
53     * contains the master partition.
54     *
55     * The result of the method is a ZNRecord, which contains a list of maps; each
56     * map is from the name of nodes to either "MASTER" or "SLAVE".
57     */
58  
59  
60    public static ZNRecord calculateIdealState(List<String> instanceNames,
61        int partitions, int replicas, String resourceName, long randomSeed)
62    {
63      return calculateIdealState(instanceNames, partitions, replicas, resourceName, randomSeed, "MASTER", "SLAVE");
64    }
65    public static ZNRecord calculateIdealState(List<String> instanceNames,
66        int partitions, int replicas, String resourceName, long randomSeed, String masterValue, String slaveValue)
67    {
68      if (instanceNames.size() <= replicas)
69      {
70        throw new IllegalArgumentException(
71            "Replicas must be less than number of storage nodes");
72      }
73  
74      Collections.sort(instanceNames);
75  
76      ZNRecord result = new ZNRecord(resourceName);
77  
78      List<Integer> partitionList = new ArrayList<Integer>(partitions);
79      for (int i = 0; i < partitions; i++)
80      {
81        partitionList.add(new Integer(i));
82      }
83      Random rand = new Random(randomSeed);
84      // Shuffle the partition list
85      Collections.shuffle(partitionList, rand);
86  
87      for (int i = 0; i < partitionList.size(); i++)
88      {
89        int partitionId = partitionList.get(i);
90        Map<String, String> partitionAssignment = new TreeMap<String, String>();
91        int masterNode = i % instanceNames.size();
92        // the first in the list is the node that contains the master
93        partitionAssignment.put(instanceNames.get(masterNode), masterValue);
94  
95        // for the jth replica, we put it on (masterNode + j) % nodes-th
96        // node
97        for (int j = 1; j <= replicas; j++)
98        {
99          int index = (masterNode + j * partitionList.size()) % instanceNames.size();
100         while(partitionAssignment.keySet().contains(instanceNames.get(index)))
101         {
102           index = (index +1) % instanceNames.size();
103         }
104         partitionAssignment
105             .put(instanceNames.get(index),
106                 slaveValue);
107       }
108       String partitionName = resourceName + "_" + partitionId;
109       result.setMapField(partitionName, partitionAssignment);
110     }
111     result.setSimpleField(IdealStateProperty.NUM_PARTITIONS.toString(), String.valueOf(partitions));
112     return result;
113   }
114 
115   public static ZNRecord calculateIdealState(List<String> instanceNames,
116       int partitions, int replicas, String resourceName)
117   {
118     long randomSeed = 888997632;
119     // seed is a constant, so that the shuffle always give same result
120     return calculateIdealState(instanceNames, partitions, replicas, resourceName,
121         randomSeed);
122   }
123 }