/**
* Copyright (C) 2014-2016 LinkedIn Corp. (pinot-core@linkedin.com)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.linkedin.pinot.controller.helix.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.helix.ZNRecord;
import org.apache.helix.model.StateModelDefinition;
import org.apache.helix.model.StateModelDefinition.StateModelDefinitionProperty;
/**
* Segment state model generator describes the transitions for segment states.
*
* Online to Offline, Online to Dropped
* Offline to Online, Offline to Dropped
*
*
*/
public class PinotHelixSegmentOnlineOfflineStateModelGenerator {
public static final String PINOT_SEGMENT_ONLINE_OFFLINE_STATE_MODEL = "SegmentOnlineOfflineStateModel";
public static final String ONLINE_STATE = "ONLINE";
public static final String OFFLINE_STATE = "OFFLINE";
public static final String DROPPED_STATE = "DROPPED";
public static final String CONSUMING_STATE = "CONSUMING";
public static StateModelDefinition generatePinotStateModelDefinition() {
StateModelDefinition.Builder builder = new StateModelDefinition.Builder(PINOT_SEGMENT_ONLINE_OFFLINE_STATE_MODEL);
builder.initialState(OFFLINE_STATE);
builder.addState(ONLINE_STATE);
builder.addState(CONSUMING_STATE);
builder.addState(OFFLINE_STATE);
builder.addState(DROPPED_STATE);
// Set the initial state when the node starts
// Add transitions between the states.
builder.addTransition(CONSUMING_STATE, ONLINE_STATE);
builder.addTransition(OFFLINE_STATE, CONSUMING_STATE);
builder.addTransition(OFFLINE_STATE, ONLINE_STATE);
builder.addTransition(CONSUMING_STATE, OFFLINE_STATE);
builder.addTransition(ONLINE_STATE, OFFLINE_STATE);
builder.addTransition(OFFLINE_STATE, DROPPED_STATE);
// set constraints on states.
// static constraint
builder.dynamicUpperBound(ONLINE_STATE, "R");
// dynamic constraint, R means it should be derived based on the replication
// factor.
builder.dynamicUpperBound(CONSUMING_STATE, "R");
StateModelDefinition statemodelDefinition = builder.build();
return statemodelDefinition;
}
/**
* This method is not used, Code is to be removed when we have the new state model running in production
* @return
*/
public static StateModelDefinition generatePinotStateModelDefinitionOld() {
ZNRecord record = new ZNRecord(PINOT_SEGMENT_ONLINE_OFFLINE_STATE_MODEL);
/*
* initial state in always offline for an instance.
*
*/
record.setSimpleField(StateModelDefinitionProperty.INITIAL_STATE.toString(), OFFLINE_STATE);
/*
* this is a ondered list of states in which we want the instances to be in. the first entry is
* given the top most priority.
*
*/
List<String> statePriorityList = new ArrayList<String>();
statePriorityList.add(ONLINE_STATE);
statePriorityList.add(OFFLINE_STATE);
statePriorityList.add(DROPPED_STATE);
record.setListField(StateModelDefinitionProperty.STATE_PRIORITY_LIST.toString(), statePriorityList);
/**
*
* If you are wondering what R and -1 signify, here is an explanation -1 means that don't even
* try to keep any instances in this state. R says that all instances in the preference list
* should be in this state.
*
*/
for (String state : statePriorityList) {
String key = state + ".meta";
Map<String, String> metadata = new HashMap<String, String>();
if (state.equals(ONLINE_STATE)) {
metadata.put("count", "R");
record.setMapField(key, metadata);
}
if (state.equals(OFFLINE_STATE)) {
metadata.put("count", "-1");
record.setMapField(key, metadata);
}
if (state.equals(DROPPED_STATE)) {
metadata.put("count", "-1");
record.setMapField(key, metadata);
}
}
/*
* construction a state transition table, this tells the controller the next state given initial
* and final states.
*
*/
for (String state : statePriorityList) {
String key = state + ".next";
if (state.equals(ONLINE_STATE)) {
Map<String, String> metadata = new HashMap<String, String>();
metadata.put(OFFLINE_STATE, OFFLINE_STATE);
metadata.put(DROPPED_STATE, DROPPED_STATE);
record.setMapField(key, metadata);
}
if (state.equals("OFFLINE")) {
Map<String, String> metadata = new HashMap<String, String>();
metadata.put(ONLINE_STATE, ONLINE_STATE);
metadata.put(DROPPED_STATE, DROPPED_STATE);
record.setMapField(key, metadata);
}
}
/*
* This is the transition priority list, again the first inserted gets the top most priority.
*
*/
List<String> stateTransitionPriorityList = new ArrayList<String>();
stateTransitionPriorityList.add("ONLINE-OFFLINE");
stateTransitionPriorityList.add("ONLINE-DROPPED");
stateTransitionPriorityList.add("OFFLINE-ONLINE");
stateTransitionPriorityList.add("OFFLINE-DROPPED");
record.setListField(StateModelDefinitionProperty.STATE_TRANSITION_PRIORITYLIST.toString(),
stateTransitionPriorityList);
throw new RuntimeException("This state model should not be used");
// return new StateModelDefinition(record);
}
}