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.5.0
Show newest version
/*
 * 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.
 */
package org.apache.solr.common.cloud;

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.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.common.util.Utils;
import org.apache.zookeeper.Watcher;
import org.noggit.JSONWriter;

/**
 * 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 final Integer znodeVersion;
  
  private final Map collectionStates;
  private Set liveNodes;

  /**
   * Use this constr when ClusterState is meant for consumption.
   */
  public ClusterState(Integer znodeVersion, Set liveNodes,
      Map collectionStates) {
    this(liveNodes, getRefMap(collectionStates),znodeVersion);
  }

  private static Map getRefMap(Map collectionStates) {
    Map collRefs =  new LinkedHashMap<>(collectionStates.size());
    for (Entry entry : collectionStates.entrySet()) {
      final DocCollection c = entry.getValue();
      collRefs.put(entry.getKey(), new CollectionRef(c));
    }
    return collRefs;
  }

  /**Use this if all the collection states are not readily available and some needs to be lazily loaded
   */
  public ClusterState(Set liveNodes, Map collectionStates, Integer znodeVersion){
    this.znodeVersion = znodeVersion;
    this.liveNodes = new HashSet<>(liveNodes.size());
    this.liveNodes.addAll(liveNodes);
    this.collectionStates = new LinkedHashMap<>(collectionStates);
  }


  /**
   * Returns a new cluster state object modified with the given collection.
   *
   * @param collectionName the name of the modified (or deleted) collection
   * @param collection     the collection object. A null value deletes the collection from the state
   * @return the updated cluster state which preserves the current live nodes and zk node version
   */
  public ClusterState copyWith(String collectionName, DocCollection collection) {
    ClusterState result = new ClusterState(liveNodes, new LinkedHashMap<>(collectionStates), znodeVersion);
    if (collection == null) {
      result.collectionStates.remove(collectionName);
    } else {
      result.collectionStates.put(collectionName, new CollectionRef(collection));
    }
    return result;
  }


  /**
   * Get the lead replica for specific collection, or null if one currently doesn't exist.
   * @deprecated Use {@link DocCollection#getLeader(String)} instead
   */
  @Deprecated
  public Replica getLeader(String collection, String sliceName) {
    DocCollection coll = getCollectionOrNull(collection);
    if (coll == null) return null;
    Slice slice = coll.getSlice(sliceName);
    if (slice == null) return null;
    return slice.getLeader();
  }

  /**
   * Returns true if the specified collection name exists, false otherwise.
   *
   * Implementation note: This method resolves the collection reference by calling
   * {@link CollectionRef#get()} which can make a call to ZooKeeper. This is necessary
   * because the semantics of how collection list is loaded have changed in SOLR-6629.
   * Please see javadocs in {@link ZkStateReader#refreshCollectionList(Watcher)}
   */
  public boolean hasCollection(String collectionName) {
    return getCollectionOrNull(collectionName) != null;
  }

  /**
   * Gets the replica by the core node 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
   *
   * @deprecated use {@link DocCollection#getReplica(String)} instead
   */
  @Deprecated
  public Replica getReplica(final String collection, final String coreNodeName) {
    DocCollection coll = getCollectionOrNull(collection);
    if (coll == null) return null;
    for (Slice slice : coll.getSlices()) {
      Replica replica = slice.getReplica(coreNodeName);
      if (replica != null) return replica;
    }
    return null;
  }

  /**
   * Get the named Slice for collection, or null if not found.
   *
   * @deprecated use {@link DocCollection#getSlice(String)} instead
   */
  @Deprecated
  public Slice getSlice(String collection, String sliceName) {
    DocCollection coll = getCollectionOrNull(collection);
    if (coll == null) return null;
    return coll.getSlice(sliceName);
  }

  /**
   * @deprecated use {@link DocCollection#getSlicesMap()} instead
   */
  @Deprecated
  public Map getSlicesMap(String collection) {
    DocCollection coll = getCollectionOrNull(collection);
    if (coll == null) return null;
    return coll.getSlicesMap();
  }

  /**
   * @deprecated use {@link DocCollection#getActiveSlicesMap()} instead
   */
  @Deprecated
  public Map getActiveSlicesMap(String collection) {
    DocCollection coll = getCollectionOrNull(collection);
    if (coll == null) return null;
    return coll.getActiveSlicesMap();
  }

  /**
   * @deprecated use {@link DocCollection#getSlices()} instead
   */
  @Deprecated
  public Collection getSlices(String collection) {
    DocCollection coll = getCollectionOrNull(collection);
    if (coll == null) return null;
    return coll.getSlices();
  }

  /**
   * @deprecated use {@link DocCollection#getActiveSlices()} instead
   */
  @Deprecated
  public Collection getActiveSlices(String collection) {
    DocCollection coll = getCollectionOrNull(collection);
    if (coll == null) return null;
    return coll.getActiveSlices();
  }


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

  public CollectionRef getCollectionRef(String coll) {
    return  collectionStates.get(coll);
  }

  /**
   * Returns the corresponding {@link DocCollection} object for the given collection name
   * if such a collection exists. Returns null otherwise.
   *
   * Implementation note: This method resolves the collection reference by calling
   * {@link CollectionRef#get()} which may make a call to ZooKeeper. This is necessary
   * because the semantics of how collection list is loaded have changed in SOLR-6629.
   * Please see javadocs in {@link ZkStateReader#refreshCollectionList(Watcher)}
   */
  public DocCollection getCollectionOrNull(String collectionName) {
    CollectionRef ref = collectionStates.get(collectionName);
    return ref == null ? null : ref.get();
  }

  /**
   * Get collection names.
   *
   * Implementation note: This method resolves the collection reference by calling
   * {@link CollectionRef#get()} which can make a call to ZooKeeper. This is necessary
   * because the semantics of how collection list is loaded have changed in SOLR-6629.
   * Please see javadocs in {@link ZkStateReader#refreshCollectionList(Watcher)}
   *
   * @deprecated use {@link #getCollectionsMap()} to avoid a second lookup for lazy collections
   */
  @Deprecated
  public Set getCollections() {
    Set result = new HashSet<>();
    for (Entry entry : collectionStates.entrySet()) {
      if (entry.getValue().get() != null) {
        result.add(entry.getKey());
      }
    }
    return result;
  }

  /**
   * Get a map of collection name vs DocCollection objects
   *
   * Implementation note: This method resolves the collection reference by calling
   * {@link CollectionRef#get()} which can make a call to ZooKeeper. This is necessary
   * because the semantics of how collection list is loaded have changed in SOLR-6629.
   * Please see javadocs in {@link ZkStateReader#refreshCollectionList(Watcher)}
   *
   * @return a map of collection name vs DocCollection object
   */
  public Map getCollectionsMap()  {
    Map result = new HashMap<>(collectionStates.size());
    for (Entry entry : collectionStates.entrySet()) {
      DocCollection collection = entry.getValue().get();
      if (collection != null) {
        result.put(entry.getKey(), collection);
      }
    }
    return result;
  }

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

  public String getShardId(String nodeName, String coreName) {
    return getShardId(null, nodeName, coreName);
  }

  public String getShardId(String collectionName, String nodeName, String coreName) {
    Collection states = collectionStates.values();
    if (collectionName != null) {
      CollectionRef c = collectionStates.get(collectionName);
      if (c != null) states = Collections.singletonList( c );
    }

    for (CollectionRef ref : states) {
      DocCollection coll = ref.get();
      if(coll == null) continue;// this collection go tremoved in between, skip
      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();
  }

  public static ClusterState load(Integer version, byte[] bytes, Set liveNodes) {
    return load(version, bytes, liveNodes, ZkStateReader.CLUSTER_STATE);
  }
  /**
   * Create ClusterState from json string that is typically stored in zookeeper.
   * 
   * @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, String znode) {
    // System.out.println("######## ClusterState.load:" + (bytes==null ? null : new String(bytes)));
    if (bytes == null || bytes.length == 0) {
      return new ClusterState(version, liveNodes, Collections.emptyMap());
    }
    Map stateMap = (Map) Utils.fromJSON(bytes);
    Map collections = new LinkedHashMap<>(stateMap.size());
    for (Entry entry : stateMap.entrySet()) {
      String collectionName = entry.getKey();
      DocCollection coll = collectionFromObjects(collectionName, (Map)entry.getValue(), version, znode);
      collections.put(collectionName, new CollectionRef(coll));
    }

    return new ClusterState( liveNodes, collections,version);
  }


  public static Aliases load(byte[] bytes) {
    if (bytes == null || bytes.length == 0) {
      return new Aliases();
    }
    Map> aliasMap = (Map>) Utils.fromJSON(bytes);

    return new Aliases(aliasMap);
  }

  // TODO move to static DocCollection.loadFromMap
  private static DocCollection collectionFromObjects(String name, Map objs, Integer version, String znode) {
    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 = Slice.loadAllFromMap(objs);
      props = Collections.emptyMap();
    } else {
      slices = Slice.loadAllFromMap(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((String) routerProps.get("name"));
    }

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

  @Override
  public void write(JSONWriter jsonWriter) {
    LinkedHashMap map = new LinkedHashMap<>();
    for (Entry e : collectionStates.entrySet()) {
      // using this class check to avoid fetching from ZK in case of lazily loaded collection
      if (e.getValue().getClass() == CollectionRef.class) {
        // check if it is a lazily loaded collection outside of clusterstate.json
        DocCollection coll = e.getValue().get();
        if (coll.getStateFormat() == 1) {
          map.put(coll.getName(),coll);
        }
      }
    }
    jsonWriter.write(map);
  }

  /**
   * The version of clusterstate.json in ZooKeeper.
   * 
   * @return null if ClusterState was created for publication, not consumption
   * @deprecated true cluster state spans many ZK nodes, stop depending on the version number of the shared node!
   */
  @Deprecated
  public Integer getZkClusterStateVersion() {
    return znodeVersion;
  }

  @Override
  public int hashCode() {
    final int prime = 31;
    int result = 1;
    result = prime * result
        + ((znodeVersion == null) ? 0 : znodeVersion.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 (znodeVersion == null) {
      if (other.znodeVersion != null) return false;
    } else if (!znodeVersion.equals(other.znodeVersion)) 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;
  }

  /**For internal use only
   */
  Map getCollectionStates() {
    return collectionStates;
  }

  public static class CollectionRef {
    private final DocCollection coll;

    public CollectionRef(DocCollection coll) {
      this.coll = coll;
    }

    public DocCollection get(){
      return coll;
    }

    public boolean isLazilyLoaded() { return false; }
    
    @Override
    public String toString() {
      if (coll != null) {
        return coll.toString();
      } else {
        return "null DocCollection ref";
      }
    }

  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy