Package org.apache.helix.controller.stages

Source Code of org.apache.helix.controller.stages.ClusterDataCache

package org.apache.helix.controller.stages;

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you 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.
*/

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.helix.HelixConstants.StateModelToken;
import org.apache.helix.HelixDataAccessor;
import org.apache.helix.HelixProperty;
import org.apache.helix.PropertyKey;
import org.apache.helix.PropertyKey.Builder;
import org.apache.helix.controller.context.ControllerContextHolder;
import org.apache.helix.model.ClusterConfiguration;
import org.apache.helix.model.ClusterConstraints;
import org.apache.helix.model.ClusterConstraints.ConstraintType;
import org.apache.helix.model.CurrentState;
import org.apache.helix.model.IdealState;
import org.apache.helix.model.InstanceConfig;
import org.apache.helix.model.LiveInstance;
import org.apache.helix.model.Message;
import org.apache.helix.model.PauseSignal;
import org.apache.helix.model.ResourceConfiguration;
import org.apache.helix.model.StateModelDefinition;
import org.apache.log4j.Logger;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

/**
* Reads the data from the cluster using data accessor. This output ClusterData which
* provides useful methods to search/lookup properties
*/
public class ClusterDataCache {
  Map<String, LiveInstance> _liveInstanceMap;
  Map<String, LiveInstance> _liveInstanceCacheMap;
  Map<String, IdealState> _idealStateMap;
  Map<String, IdealState> _idealStateCacheMap;
  Map<String, StateModelDefinition> _stateModelDefMap;
  Map<String, InstanceConfig> _instanceConfigMap;
  Map<String, InstanceConfig> _instanceConfigCacheMap;
  Map<String, ClusterConstraints> _constraintMap;
  Map<String, ClusterConstraints> _constraintCacheMap;
  Map<String, Map<String, Map<String, CurrentState>>> _currentStateMap;
  Map<String, Map<String, Message>> _messageMap;
  Map<String, Map<String, String>> _idealStateRuleMap;
  Map<String, ResourceConfiguration> _resourceConfigMap;
  Map<String, ResourceConfiguration> _resourceConfigCacheMap;
  Map<String, ControllerContextHolder> _controllerContextMap;
  PauseSignal _pause;
  LiveInstance _leader;
  ClusterConfiguration _clusterConfig;
  boolean _writeAssignments;

  // maintain a cache of participant messages across pipeline runs
  Map<String, Map<String, Message>> _messageCache = Maps.newHashMap();

  boolean _init = true;

  private static final Logger LOG = Logger.getLogger(ClusterDataCache.class.getName());

  /**
   * This refreshes the cluster data by re-fetching the data from zookeeper in
   * an efficient way
   * @param accessor
   * @return
   */
  public synchronized boolean refresh(HelixDataAccessor accessor) {
    LOG.info("START: ClusterDataCache.refresh()");
    long startTime = System.currentTimeMillis();

    Builder keyBuilder = accessor.keyBuilder();

    if (_init) {
      _idealStateCacheMap = accessor.getChildValuesMap(keyBuilder.idealStates());
      _liveInstanceCacheMap = accessor.getChildValuesMap(keyBuilder.liveInstances());
      _instanceConfigCacheMap = accessor.getChildValuesMap(keyBuilder.instanceConfigs());
      _resourceConfigCacheMap = accessor.getChildValuesMap(keyBuilder.resourceConfigs());
      _constraintCacheMap = accessor.getChildValuesMap(keyBuilder.constraints());
    }
    _idealStateMap = Maps.newHashMap(_idealStateCacheMap);
    _liveInstanceMap = Maps.newHashMap(_liveInstanceCacheMap);
    _instanceConfigMap = Maps.newHashMap(_instanceConfigCacheMap);
    _resourceConfigMap = Maps.newHashMap(_resourceConfigCacheMap);
    _constraintMap = Maps.newHashMap(_constraintCacheMap);

    for (LiveInstance instance : _liveInstanceMap.values()) {
      LOG.trace("live instance: " + instance.getInstanceName() + " " + instance.getSessionId());
    }

    _stateModelDefMap = accessor.getChildValuesMap(keyBuilder.stateModelDefs());

    Map<String, Map<String, Message>> msgMap = new HashMap<String, Map<String, Message>>();
    List<PropertyKey> newMessageKeys = Lists.newLinkedList();
    long purgeSum = 0;
    for (String instanceName : _liveInstanceMap.keySet()) {
      // get the cache
      Map<String, Message> cachedMap = _messageCache.get(instanceName);
      if (cachedMap == null) {
        cachedMap = Maps.newHashMap();
        _messageCache.put(instanceName, cachedMap);
      }
      msgMap.put(instanceName, cachedMap);

      // get the current names
      Set<String> messageNames =
          Sets.newHashSet(accessor.getChildNames(keyBuilder.messages(instanceName)));

      long purgeStart = System.currentTimeMillis();
      // clear stale names
      Iterator<String> cachedNamesIter = cachedMap.keySet().iterator();
      while (cachedNamesIter.hasNext()) {
        String messageName = cachedNamesIter.next();
        if (!messageNames.contains(messageName)) {
          cachedNamesIter.remove();
        }
      }
      long purgeEnd = System.currentTimeMillis();
      purgeSum += purgeEnd - purgeStart;

      // get the keys for the new messages
      for (String messageName : messageNames) {
        if (!cachedMap.containsKey(messageName)) {
          newMessageKeys.add(keyBuilder.message(instanceName, messageName));
        }
      }
    }

    // get the new messages
    if (newMessageKeys.size() > 0) {
      List<Message> newMessages = accessor.getProperty(newMessageKeys);
      for (Message message : newMessages) {
        if (message != null) {
          Map<String, Message> cachedMap = _messageCache.get(message.getTgtName());
          cachedMap.put(message.getId(), message);
        }
      }
    }
    _messageMap = Collections.unmodifiableMap(msgMap);
    LOG.debug("Purge took: " + purgeSum);

    List<PropertyKey> currentStateKeys = Lists.newLinkedList();
    Map<String, Map<String, Map<String, CurrentState>>> allCurStateMap =
        new HashMap<String, Map<String, Map<String, CurrentState>>>();
    for (String instanceName : _liveInstanceMap.keySet()) {
      LiveInstance liveInstance = _liveInstanceMap.get(instanceName);
      String sessionId = liveInstance.getSessionId();
      List<String> currentStateNames =
          accessor.getChildNames(keyBuilder.currentStates(instanceName, sessionId));
      for (String currentStateName : currentStateNames) {
        currentStateKeys.add(keyBuilder.currentState(instanceName, sessionId, currentStateName));
      }

      // ensure an empty current state map for all live instances and sessions
      Map<String, Map<String, CurrentState>> instanceCurStateMap = allCurStateMap.get(instanceName);
      if (instanceCurStateMap == null) {
        instanceCurStateMap = Maps.newHashMap();
        allCurStateMap.put(instanceName, instanceCurStateMap);
      }
      Map<String, CurrentState> sessionCurStateMap = instanceCurStateMap.get(sessionId);
      if (sessionCurStateMap == null) {
        sessionCurStateMap = Maps.newHashMap();
        instanceCurStateMap.put(sessionId, sessionCurStateMap);
      }
    }
    List<CurrentState> currentStates = accessor.getProperty(currentStateKeys);
    Iterator<PropertyKey> csKeyIter = currentStateKeys.iterator();
    for (CurrentState currentState : currentStates) {
      PropertyKey key = csKeyIter.next();
      String[] params = key.getParams();
      if (currentState != null && params.length >= 4) {
        Map<String, Map<String, CurrentState>> instanceCurStateMap = allCurStateMap.get(params[1]);
        Map<String, CurrentState> sessionCurStateMap = instanceCurStateMap.get(params[2]);
        sessionCurStateMap.put(params[3], currentState);
      }
    }

    for (String instance : allCurStateMap.keySet()) {
      allCurStateMap.put(instance, Collections.unmodifiableMap(allCurStateMap.get(instance)));
    }
    _currentStateMap = Collections.unmodifiableMap(allCurStateMap);

    // New in 0.7: Read more information for the benefit of user-defined rebalancers
    _controllerContextMap = accessor.getChildValuesMap(keyBuilder.controllerContexts());

    // Read all single properties together
    List<HelixProperty> singleProperties =
        accessor.getProperty(ImmutableList.of(keyBuilder.clusterConfig(),
            keyBuilder.controllerLeader(), keyBuilder.pause()));
    _clusterConfig = (ClusterConfiguration) singleProperties.get(0);
    if (_clusterConfig != null) {
      _idealStateRuleMap = _clusterConfig.getIdealStateRules();
    } else {
      _idealStateRuleMap = Collections.emptyMap();
    }
    _leader = (LiveInstance) singleProperties.get(1);
    _pause = (PauseSignal) singleProperties.get(2);

    long endTime = System.currentTimeMillis();
    LOG.info("END: ClusterDataCache.refresh(), took " + (endTime - startTime) + " ms");

    if (LOG.isDebugEnabled()) {
      int numPaths =
          _liveInstanceMap.size() + _idealStateMap.size() + +_resourceConfigMap.size()
              + _stateModelDefMap.size() + _instanceConfigMap.size() + _constraintMap.size()
              + _controllerContextMap.size() + newMessageKeys.size() + currentStateKeys.size();
      LOG.debug("Paths read: " + numPaths);
    }

    _init = false;
    return true;
  }

  /**
   * Get the live instance associated with the controller leader
   * @return LiveInstance
   */
  public LiveInstance getLeader() {
    return _leader;
  }

  /**
   * Get the pause signal (if any)
   * @return PauseSignal
   */
  public PauseSignal getPauseSignal() {
    return _pause;
  }

  /**
   * Retrieves the configs for all resources
   * @return
   */
  public Map<String, ResourceConfiguration> getResourceConfigs() {
    return _resourceConfigMap;
  }

  /**
   * Retrieves the idealstates for all resources
   * @return
   */
  public Map<String, IdealState> getIdealStates() {
    return _idealStateMap;
  }

  public synchronized void setIdealStates(List<IdealState> idealStates) {
    Map<String, IdealState> idealStateMap = Maps.newHashMap();
    for (IdealState idealState : idealStates) {
      idealStateMap.put(idealState.getId(), idealState);
    }
    _idealStateCacheMap = idealStateMap;
  }

  public Map<String, Map<String, String>> getIdealStateRules() {
    return _idealStateRuleMap;
  }

  /**
   * Returns the LiveInstances for each of the instances that are curretnly up and running
   * @return
   */
  public Map<String, LiveInstance> getLiveInstances() {
    return _liveInstanceMap;
  }

  public synchronized void setLiveInstances(List<LiveInstance> liveInstances) {
    Map<String, LiveInstance> liveInstanceMap = Maps.newHashMap();
    for (LiveInstance liveInstance : liveInstances) {
      liveInstanceMap.put(liveInstance.getId(), liveInstance);
    }
    _liveInstanceCacheMap = liveInstanceMap;
  }

  /**
   * Provides the current state of the node for a given session id,
   * the sessionid can be got from LiveInstance
   * @param instanceName
   * @param clientSessionId
   * @return
   */
  public Map<String, CurrentState> getCurrentState(String instanceName, String clientSessionId) {
    if (!_currentStateMap.containsKey(instanceName)
        || !_currentStateMap.get(instanceName).containsKey(clientSessionId)) {
      return Collections.emptyMap();
    }
    return _currentStateMap.get(instanceName).get(clientSessionId);
  }

  /**
   * Provides a list of current outstanding transitions on a given instance.
   * @param instanceName
   * @return
   */
  public Map<String, Message> getMessages(String instanceName) {
    Map<String, Message> map = _messageMap.get(instanceName);
    if (map != null) {
      return map;
    } else {
      return Collections.emptyMap();
    }
  }

  /**
   * Provides all outstanding messages
   * @return
   */
  public Map<String, Map<String, Message>> getMessageMap() {
    return _messageMap;
  }

  /**
   * Provides the state model definition for a given state model
   * @param stateModelDefRef
   * @return
   */
  public StateModelDefinition getStateModelDef(String stateModelDefRef) {
    return _stateModelDefMap.get(stateModelDefRef);
  }

  /**
   * Provides all state model definitions
   * @return state model definition map
   */
  public Map<String, StateModelDefinition> getStateModelDefMap() {
    return _stateModelDefMap;
  }

  /**
   * Provides the idealstate for a given resource
   * @param resourceName
   * @return
   */
  public IdealState getIdealState(String resourceName) {
    return _idealStateMap.get(resourceName);
  }

  /**
   * Returns the instance config map
   * @return
   */
  public Map<String, InstanceConfig> getInstanceConfigMap() {
    return _instanceConfigMap;
  }

  public synchronized void setInstanceConfigs(List<InstanceConfig> instanceConfigs) {
    Map<String, InstanceConfig> instanceConfigMap = Maps.newHashMap();
    for (InstanceConfig instanceConfig : instanceConfigs) {
      instanceConfigMap.put(instanceConfig.getId(), instanceConfig);
    }
    _instanceConfigCacheMap = instanceConfigMap;
  }

  public synchronized void setResourceConfigs(List<ResourceConfiguration> resourceConfigs) {
    Map<String, ResourceConfiguration> resourceConfigMap = Maps.newHashMap();
    for (ResourceConfiguration resourceConfig : resourceConfigs) {
      resourceConfigMap.put(resourceConfig.getId(), resourceConfig);
    }
    _resourceConfigCacheMap = resourceConfigMap;
  }

  public synchronized void setConstraints(List<ClusterConstraints> constraints) {
    Map<String, ClusterConstraints> constraintMap = Maps.newHashMap();
    for (ClusterConstraints constraint : constraints) {
      constraintMap.put(constraint.getId(), constraint);
    }
    _constraintCacheMap = constraintMap;
  }

  /**
   * Some partitions might be disabled on specific nodes.
   * This method allows one to fetch the set of nodes where a given partition is disabled
   * @param partition
   * @return
   */
  public Set<String> getDisabledInstancesForPartition(String partition) {
    Set<String> disabledInstancesSet = new HashSet<String>();
    for (String instance : _instanceConfigMap.keySet()) {
      InstanceConfig config = _instanceConfigMap.get(instance);
      if (config.getInstanceEnabled() == false
          || config.getInstanceEnabledForPartition(partition) == false) {
        disabledInstancesSet.add(instance);
      }
    }
    return disabledInstancesSet;
  }

  /**
   * Returns the number of replicas for a given resource.
   * @param resourceName
   * @return
   */
  public int getReplicas(String resourceName) {
    int replicas = -1;

    if (_idealStateMap.containsKey(resourceName)) {
      String replicasStr = _idealStateMap.get(resourceName).getReplicas();

      if (replicasStr != null) {
        if (replicasStr.equals(StateModelToken.ANY_LIVEINSTANCE.toString())) {
          replicas = _liveInstanceMap.size();
        } else {
          try {
            replicas = Integer.parseInt(replicasStr);
          } catch (Exception e) {
            LOG.error("invalid replicas string: " + replicasStr);
          }
        }
      } else {
        LOG.error("idealState for resource: " + resourceName + " does NOT have replicas");
      }
    }
    return replicas;
  }

  /**
   * Returns the ClusterConstraints for a given constraintType
   * @param type
   * @return
   */
  public ClusterConstraints getConstraint(ConstraintType type) {
    if (_constraintMap != null) {
      return _constraintMap.get(type.toString());
    }
    return null;
  }

  public Map<String, ClusterConstraints> getConstraintMap() {
    return _constraintMap;
  }

  public Map<String, ControllerContextHolder> getContextMap() {
    return _controllerContextMap;
  }

  public ClusterConfiguration getClusterConfig() {
    return _clusterConfig;
  }

  public void cacheMessages(List<Message> messages) {
    for (Message message : messages) {
      String instanceName = message.getTgtName();
      Map<String, Message> instMsgMap = null;
      if (_messageCache.containsKey(instanceName)) {
        instMsgMap = _messageCache.get(instanceName);
      } else {
        instMsgMap = Maps.newHashMap();
        _messageCache.put(instanceName, instMsgMap);
      }
      instMsgMap.put(message.getId(), message);
    }
  }

  /**
   * Enable or disable writing resource assignments
   * @param enable true to enable, false to disable
   */
  public void setAssignmentWritePolicy(boolean enable) {
    _writeAssignments = enable;
  }

  /**
   * Check if writing resource assignments is enabled
   * @return true if enabled, false if disabled
   */
  public boolean assignmentWriteEnabled() {
    return _writeAssignments;
  }

  /**
   * Indicate that a full read should be done on the next refresh
   */
  public synchronized void requireFullRefresh() {
    _init = true;
  }

  /**
   * toString method to print the entire cluster state
   */
  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("liveInstaceMap:" + _liveInstanceMap).append("\n");
    sb.append("idealStateMap:" + _idealStateMap).append("\n");
    sb.append("stateModelDefMap:" + _stateModelDefMap).append("\n");
    sb.append("instanceConfigMap:" + _instanceConfigMap).append("\n");
    sb.append("messageMap:" + _messageMap).append("\n");

    return sb.toString();
  }
}
TOP

Related Classes of org.apache.helix.controller.stages.ClusterDataCache

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.