blob: 282f6708133120a325f145d225273639a26bef43 [file] [log] [blame]
/*
* 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.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.apache.solr.common.util.Utils;
public class Replica extends ZkNodeProps {
/**
* The replica's state. In general, if the node the replica is hosted on is
* not under {@code /live_nodes} in ZK, the replica's state should be
* discarded.
*/
public enum State {
/**
* The replica is ready to receive updates and queries.
* <p>
* <b>NOTE</b>: when the node the replica is hosted on crashes, the
* replica's state may remain ACTIVE in ZK. To determine if the replica is
* truly active, you must also verify that its {@link Replica#getNodeName()
* node} is under {@code /live_nodes} in ZK (or use
* {@link ZkStateReader#isNodeLive(String)} (String)}).
* </p>
*/
ACTIVE,
LEADER,
/**
* The first state before {@link State#RECOVERING}. A node in this state
* should be actively trying to move to {@link State#RECOVERING}.
* <p>
* <b>NOTE</b>: a replica's state may appear DOWN in ZK also when the node
* it's hosted on gracefully shuts down. This is a best effort though, and
* should not be relied on.
* </p>
*/
DOWN,
/**
* The node is recovering from the leader. This might involve peer-sync,
* full replication or finding out things are already in sync.
*/
RECOVERING,
BUFFERING,
/**
* Recovery attempts have not worked, something is not right.
* <p>
* <b>NOTE</b>: This state doesn't matter if the node is not part of
* {@code /live_nodes} in ZK; in that case the node is not part of the
* cluster and it's state should be discarded.
* </p>
*/
RECOVERY_FAILED;
@Override
public String toString() {
return super.toString().toLowerCase(Locale.ROOT);
}
public static String getShortState(State state) {
if (state.equals(RECOVERY_FAILED)) {
return "f";
}
return state.toString().substring(0,1).toLowerCase(Locale.ROOT);
}
public static State shortStateToState(String shortState) {
if (shortState.equals("a")) {
return State.ACTIVE;
} if (shortState.equals("l")) {
return State.LEADER;
} else if (shortState.equals("r")) {
return State.RECOVERING;
} else if (shortState.equals("b")) {
return State.BUFFERING;
} else if (shortState.equals("d")) {
return State.DOWN;
} else if (shortState.equals("f")) {
return State.RECOVERY_FAILED;
}
throw new IllegalStateException("Unknown state: " + shortState);
}
/** Converts the state string to a State instance. */
public static State getState(String stateStr) {
return stateStr == null ? null : State.valueOf(stateStr.toUpperCase(Locale.ROOT));
}
}
public enum Type {
/**
* Writes updates to transaction log and indexes locally. Replicas of type {@link Type#NRT} support NRT (soft commits) and RTG.
* Any {@link Type#NRT} replica can become a leader. A shard leader will forward updates to all active {@link Type#NRT} and
* {@link Type#TLOG} replicas.
*/
NRT,
/**
* Writes to transaction log, but not to index, uses replication. Any {@link Type#TLOG} replica can become leader (by first
* applying all local transaction log elements). If a replica is of type {@link Type#TLOG} but is also the leader, it will behave
* as a {@link Type#NRT}. A shard leader will forward updates to all active {@link Type#NRT} and {@link Type#TLOG} replicas.
*/
TLOG,
/**
* Doesn’t index or writes to transaction log. Just replicates from {@link Type#NRT} or {@link Type#TLOG} replicas. {@link Type#PULL}
* replicas can’t become shard leaders (i.e., if there are only pull replicas in the collection at some point, updates will fail
* same as if there is no leaders, queries continue to work), so they don’t even participate in elections.
*/
PULL;
public static Type get(String name){
return name == null ? Type.NRT : Type.valueOf(name.toUpperCase(Locale.ROOT));
}
}
public interface NodeNameToBaseUrl {
String getBaseUrlForNodeName(final String nodeName);
}
private final String name;
private final String nodeName;
private State state;
private final Type type;
public final String slice, collection;
private final String baseUrl;
public Replica(String name, Map<String,Object> propMap, String collection, Long collectionId, String slice, NodeNameToBaseUrl nodeNameToBaseUrl) {
super(propMap);
this.collection = collection;
this.slice = slice;
this.name = name;
this.nodeName = (String) propMap.get(ZkStateReader.NODE_NAME_PROP);
String rawId = (String) propMap.get("id");
if (rawId != null && !rawId.contains(":")) {
this.id = Long.parseLong(rawId);
}
this.collectionId = collectionId;
this.baseUrl = nodeNameToBaseUrl.getBaseUrlForNodeName(this.nodeName);
type = Type.get((String) propMap.get(ZkStateReader.REPLICA_TYPE));
Objects.requireNonNull(this.collection, "'collection' must not be null");
Objects.requireNonNull(this.slice, "'slice' must not be null");
Objects.requireNonNull(this.name, "'name' must not be null");
Objects.requireNonNull(this.nodeName, "'node_name' must not be null");
Objects.requireNonNull(this.type, "'type' must not be null");
Objects.requireNonNull(this.collectionId, "'collectionId' must not be null");
if (propMap.get(ZkStateReader.STATE_PROP) != null) {
if (propMap.get(ZkStateReader.STATE_PROP) instanceof State) {
this.state = (State) propMap.get(ZkStateReader.STATE_PROP);
} else {
this.state = State.getState((String) propMap.get(ZkStateReader.STATE_PROP));
}
} else {
this.state = State.DOWN; //Default to DOWN
propMap.put(ZkStateReader.STATE_PROP, state.toString());
}
}
public Replica(String name, Map<String,Object> propMap, String collection, Long collectionId, String slice, String baseUrl) {
super(propMap);
this.collection = collection;
this.slice = slice;
this.name = name;
this.nodeName = (String) propMap.get(ZkStateReader.NODE_NAME_PROP);
this.id = propMap.containsKey("id") ? Long.parseLong((String) propMap.get("id")) : null;
this.collectionId = collectionId;
Objects.requireNonNull(this.collectionId, "'collectionId' must not be null");
this.baseUrl = baseUrl;
type = Type.get((String) propMap.get(ZkStateReader.REPLICA_TYPE));
if (propMap.get(ZkStateReader.STATE_PROP) != null) {
if (propMap.get(ZkStateReader.STATE_PROP) instanceof State) {
this.state = (State) propMap.get(ZkStateReader.STATE_PROP);
} else {
this.state = State.getState((String) propMap.get(ZkStateReader.STATE_PROP));
}
} else {
this.state = State.DOWN; //Default to DOWN
propMap.put(ZkStateReader.STATE_PROP, state.toString());
}
}
Long id;
final Long collectionId;
public String getId() {
return collectionId + "-" + (id == null ? null : id.toString());
}
public String getInternalId() {
return id.toString();
}
public Long getCollectionId() {
return collectionId;
}
public String getCollection(){
return collection;
}
public String getSlice(){
return slice;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
Replica replica = (Replica) o;
return name.equals(replica.name) && nodeName.equals(replica.nodeName);
}
@Override
public int hashCode() {
return Objects.hash(name, nodeName);
}
/** Also known as coreNodeName. */
public String getName() {
return name;
}
public String getCoreUrl() {
return getCoreUrl(getBaseUrl(), name);
}
public String getBaseUrl() {
return baseUrl;
}
/** The name of the node this replica resides on */
public String getNodeName() {
return nodeName;
}
/** Returns the {@link State} of this replica. */
public State getState() {
return state;
}
// only to be used by ZkStateWriter currently
public void setState(State state) {
this.state = state;
}
public boolean isActive(Set<String> liveNodes) {
return this.nodeName != null && liveNodes.contains(this.nodeName) && this.state == State.ACTIVE;
}
public Type getType() {
return this.type;
}
public String getProperty(String propertyName) {
final String propertyKey;
if (!propertyName.startsWith(ZkStateReader.PROPERTY_PROP_PREFIX)) {
propertyKey = ZkStateReader.PROPERTY_PROP_PREFIX + propertyName;
} else {
propertyKey = propertyName;
}
final String propertyValue = getStr(propertyKey);
return propertyValue;
}
public static String getCoreUrl(String baseUrl, String coreName) {
StringBuilder sb = new StringBuilder(baseUrl.length() + coreName.length() + 1);
sb.append(baseUrl);
if (!baseUrl.endsWith("/")) sb.append("/");
sb.append(coreName);
return sb.toString();
}
@Override
public String toString() {
return name + "(" + getId() + ")" + ':' + Utils.toJSONString(propMap); // small enough, keep it on one line (i.e. no indent)
}
}