All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.common.cloud.ClusterState Maven / Gradle / Ivy

There is a newer version: 9.8.1
Show newest version
package org.apache.solr.common.cloud;

/*
 * 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.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.noggit.JSONWriter;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Immutable state of the cloud. Normally you can get the state by using
 * {@link ZkStateReader#getClusterState()}.
 * @lucene.experimental
 */
public class ClusterState implements JSONWriter.Writable {
  private static Logger log = LoggerFactory.getLogger(ClusterState.class);
  
  private Integer zkClusterStateVersion;
  
  private final Map collectionStates;  // Map>
  private Set liveNodes;
  private final ZkStateReader stateReader;


  /**
   * Use this constr when ClusterState is meant for publication.
   * 
   * hashCode and equals will only depend on liveNodes and not clusterStateVersion.
   */
  public ClusterState(Set liveNodes,
      Map collectionStates) {
    this(null, liveNodes, collectionStates, null);
  }

  /**
   * @deprecated prefer another constructor
   */
  public ClusterState(Integer zkClusterStateVersion, Set liveNodes,
                      Map collectionStates) {
    this(zkClusterStateVersion, liveNodes, collectionStates, null);

  }
  
  /**
   * Use this constr when ClusterState is meant for consumption.
   */
  public ClusterState(Integer zkClusterStateVersion, Set liveNodes,
      Map collectionStates, ZkStateReader stateReader) {
    this.zkClusterStateVersion = zkClusterStateVersion;
    this.liveNodes = new HashSet(liveNodes.size());
    this.liveNodes.addAll(liveNodes);
    this.collectionStates = new LinkedHashMap(collectionStates.size());
    this.collectionStates.putAll(collectionStates);
    this.stateReader = stateReader;

  }

  public ClusterState copyWith(Map modified){
    ClusterState result = new ClusterState(zkClusterStateVersion, liveNodes,collectionStates,stateReader);
    for (Entry e : modified.entrySet()) {
      DocCollection c = e.getValue();
      if(c == null) {
        result.collectionStates.remove(e.getKey());
        continue;
      }
      result.collectionStates.put(c.getName(), c);
    }
    return result;
  }


  /**
   * Get the lead replica for specific collection, or null if one currently doesn't exist.
   */
  public Replica getLeader(String collection, String sliceName) {
    DocCollection coll = collectionStates.get(collection);
    if (coll == null) return null;
    Slice slice = coll.getSlice(sliceName);
    if (slice == null) return null;
    return slice.getLeader();
  }
  
  /**
   * Gets the replica by the core name (assuming the slice is unknown) or null if replica is not found.
   * If the slice is known, do not use this method.
   * coreNodeName is the same as replicaName
   */
  public Replica getReplica(final String collection, final String coreNodeName) {
    return getReplica(collectionStates.get(collection), coreNodeName);
  }

  private Replica getReplica(DocCollection coll, String replicaName) {
    if (coll == null) return null;
    for(Slice slice: coll.getSlices()) {
      Replica replica = slice.getReplica(replicaName);
      if (replica != null) return replica;
    }
    return null;
  }
  public boolean hasCollection(String coll){
    return collectionStates.get(coll)!=null;
  }


  /**
   * Get the named Slice for collection, or null if not found.
   */
  public Slice getSlice(String collection, String sliceName) {
    DocCollection coll = collectionStates.get(collection);
    if (coll == null) return null;
    return coll.getSlice(sliceName);
  }

  public Map getSlicesMap(String collection) {
    DocCollection coll = collectionStates.get(collection);
    if (coll == null) return null;
    return coll.getSlicesMap();
  }
  
  public Map getActiveSlicesMap(String collection) {
    DocCollection coll = collectionStates.get(collection);
    if (coll == null) return null;
    return coll.getActiveSlicesMap();
  }

  public Collection getSlices(String collection) {
    DocCollection coll = collectionStates.get(collection);
    if (coll == null) return null;
    return coll.getSlices();
  }

  public Collection getActiveSlices(String collection) {
    DocCollection coll = collectionStates.get(collection);
    if (coll == null) return null;
    return coll.getActiveSlices();
  }

  public DocCollection getCollectionOrNull(String collection) {
    return collectionStates.get(collection);

  }
  /**
   * Get the named DocCollection object, or throw an exception if it doesn't exist.
   */
  public DocCollection getCollection(String collection) {
    DocCollection coll = collectionStates.get(collection);
    if (coll == null) {
      throw new SolrException(ErrorCode.BAD_REQUEST, "Could not find collection:" + collection);
    }
    return coll;
  }

  /**
   * Get collection names.
   */
  public Set getCollections() {
    return Collections.unmodifiableSet(collectionStates.keySet());
  }

  /**
   * @return Map<collectionName, Map<sliceName,Slice>>
   */
  @Deprecated
  public Map getCollectionStates() {
    return Collections.unmodifiableMap(collectionStates);
  }

  /**
   * Get names of the currently live nodes.
   */
  public Set getLiveNodes() {
    return Collections.unmodifiableSet(liveNodes);
  }

  public String getShardId(String nodeName, String coreName) {
    // System.out.println("###### getShardId(" + baseUrl + "," + coreName + ") in " + collectionStates);
    for (DocCollection coll : collectionStates.values()) {
      for (Slice slice : coll.getSlices()) {
        for (Replica replica : slice.getReplicas()) {
          // TODO: for really large clusters, we could 'index' on this
          String rnodeName = replica.getStr(ZkStateReader.NODE_NAME_PROP);
          String rcore = replica.getStr(ZkStateReader.CORE_NAME_PROP);
          if (nodeName.equals(rnodeName) && coreName.equals(rcore)) {
            return slice.getName();
          }
        }
      }
    }
    return null;
  }

  /**
   * Check if node is alive. 
   */
  public boolean liveNodesContain(String name) {
    return liveNodes.contains(name);
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append("live nodes:" + liveNodes);
    sb.append(" collections:" + collectionStates);
    return sb.toString();
  }

  /**
   * Create ClusterState by reading the current state from zookeeper. 
   */
  public static ClusterState load(SolrZkClient zkClient, Set liveNodes, ZkStateReader stateReader) throws KeeperException, InterruptedException {
    Stat stat = new Stat();
    byte[] state = zkClient.getData(ZkStateReader.CLUSTER_STATE,
        null, stat, true);
    return load(stat.getVersion(), state, liveNodes, stateReader);
  }
  
 
  /**
   * Create ClusterState from json string that is typically stored in zookeeper.
   * 
   * Use {@link ClusterState#load(SolrZkClient, Set, ZkStateReader)} instead, unless you want to
   * do something more when getting the data - such as get the stat, set watch, etc.
   * @param version zk version of the clusterstate.json file (bytes)
   * @param bytes clusterstate.json as a byte array
   * @param liveNodes list of live nodes
   * @return the ClusterState
   */
  public static ClusterState load(Integer version, byte[] bytes, Set liveNodes, ZkStateReader stateReader) {
    // System.out.println("######## ClusterState.load:" + (bytes==null ? null : new String(bytes)));
    if (bytes == null || bytes.length == 0) {
      return new ClusterState(version, liveNodes, Collections.emptyMap(),stateReader);
    }
    Map stateMap = (Map) ZkStateReader.fromJSON(bytes);
    Map collections = new LinkedHashMap(stateMap.size());
    for (Entry entry : stateMap.entrySet()) {
      String collectionName = entry.getKey();
      DocCollection coll = collectionFromObjects(collectionName, (Map)entry.getValue());
      collections.put(collectionName, coll);
    }

    // System.out.println("######## ClusterState.load result:" + collections);
    return new ClusterState(version, liveNodes, collections);
  }
  
  public static Aliases load(byte[] bytes) {
    if (bytes == null || bytes.length == 0) {
      return new Aliases();
    }
    Map> aliasMap = (Map>) ZkStateReader.fromJSON(bytes);

    return new Aliases(aliasMap);
  }

  private static DocCollection collectionFromObjects(String name, Map objs) {
    Map props;
    Map slices;

    Map sliceObjs = (Map)objs.get(DocCollection.SHARDS);
    if (sliceObjs == null) {
      // legacy format from 4.0... there was no separate "shards" level to contain the collection shards.
      slices = makeSlices(objs);
      props = Collections.emptyMap();
    } else {
      slices = makeSlices(sliceObjs);
      props = new HashMap(objs);
      objs.remove(DocCollection.SHARDS);
    }

    Object routerObj = props.get(DocCollection.DOC_ROUTER);
    DocRouter router;
    if (routerObj == null) {
      router = DocRouter.DEFAULT;
    } else if (routerObj instanceof String) {
      // back compat with Solr4.4
      router = DocRouter.getDocRouter((String)routerObj);
    } else {
      Map routerProps = (Map)routerObj;
      router = DocRouter.getDocRouter(routerProps.get("name"));
    }

    return new DocCollection(name, slices, props, router);
  }

  private static Map makeSlices(Map genericSlices) {
    if (genericSlices == null) return Collections.emptyMap();
    Map result = new LinkedHashMap(genericSlices.size());
    for (Map.Entry entry : genericSlices.entrySet()) {
      String name = entry.getKey();
      Object val = entry.getValue();
      if (val instanceof Slice) {
        result.put(name, (Slice)val);
      } else if (val instanceof Map) {
        result.put(name, new Slice(name, null, (Map)val));
      }
    }
    return result;
  }

  @Override
  public void write(JSONWriter jsonWriter) {
    jsonWriter.write(collectionStates);
  }

  /**
   * The version of clusterstate.json in ZooKeeper.
   * 
   * @return null if ClusterState was created for publication, not consumption
   */
  public Integer getZkClusterStateVersion() {
    return zkClusterStateVersion;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
        + ((zkClusterStateVersion == null) ? 0 : zkClusterStateVersion.hashCode());
    result = prime * result + ((liveNodes == null) ? 0 : liveNodes.hashCode());
    return result;
  }

  @Override
  public boolean equals(Object obj) {
    if (this == obj) return true;
    if (obj == null) return false;
    if (getClass() != obj.getClass()) return false;
    ClusterState other = (ClusterState) obj;
    if (zkClusterStateVersion == null) {
      if (other.zkClusterStateVersion != null) return false;
    } else if (!zkClusterStateVersion.equals(other.zkClusterStateVersion)) return false;
    if (liveNodes == null) {
      if (other.liveNodes != null) return false;
    } else if (!liveNodes.equals(other.liveNodes)) return false;
    return true;
  }

  /**
   * Internal API used only by ZkStateReader
   */
  void setLiveNodes(Set liveNodes){
    this.liveNodes = liveNodes;
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy