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

org.apache.kudu.client.RemoteTablet Maven / Gradle / Ivy

Go to download

org.apache.kudu:kudu-client with netty package relocations reverted and netty classes stripped away so that camel-quarkus-kudu can use quarkus-netty as a replacement

There is a newer version: 3.15.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.kudu.client;

import static java.nio.charset.StandardCharsets.UTF_8;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;

import com.google.common.base.Joiner;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.ImmutableList;
import org.apache.yetus.audience.InterfaceAudience;
import org.apache.yetus.audience.InterfaceStability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.apache.kudu.consensus.Metadata;

/**
 * This class encapsulates the information regarding a tablet and its locations.
 * 

* RemoteTablet's main function is to keep track of where the leader for this * tablet is. For example, an RPC might call {@link #getLeaderServerInfo()}, contact that TS, find * it's not the leader anymore, and then call {@link #demoteLeader(String)}. *

* A RemoteTablet's life is expected to be long in a cluster where roles aren't changing often, * and short when they do since the Kudu client will replace the RemoteTablet it caches with new * ones after getting tablet locations from the master. */ @InterfaceAudience.Private @InterfaceStability.Unstable public class RemoteTablet implements Comparable { private static final Logger LOG = LoggerFactory.getLogger(RemoteTablet.class); // This random integer is used when making any random choice for replica // selection. It is static to provide a deterministic selection for any given // process and therefore also better cache affinity while ensuring that we can // still benefit from spreading the load across replicas for other processes // and applications. private static final int RANDOM_SELECTION_INT = new Random().nextInt(Integer.MAX_VALUE); private final String tableId; private final String tabletId; @GuardedBy("tabletServers") private final Map tabletServers; private final AtomicReference> replicas = new AtomicReference<>(ImmutableList.of()); private final Partition partition; @GuardedBy("tabletServers") private String leaderUuid; RemoteTablet(String tableId, String tabletId, Partition partition, List replicas, List serverInfos) { Preconditions.checkArgument(replicas.size() == serverInfos.size(), "the number of replicas does not equal the number of servers"); this.tabletId = tabletId; this.tableId = tableId; this.partition = partition; this.tabletServers = new HashMap<>(serverInfos.size()); for (ServerInfo serverInfo : serverInfos) { this.tabletServers.put(serverInfo.getUuid(), serverInfo); } ImmutableList.Builder replicasBuilder = new ImmutableList.Builder<>(); for (int i = 0; i < replicas.size(); ++i) { replicasBuilder.add(replicas.get(i)); if (replicas.get(i).getRoleAsEnum().equals(Metadata.RaftPeerPB.Role.LEADER)) { this.leaderUuid = serverInfos.get(i).getUuid(); } } if (leaderUuid == null) { LOG.warn("No leader provided for tablet {}", getTabletId()); } this.replicas.set(replicasBuilder.build()); } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append(tabletId).append("@["); List tsStrings; synchronized (tabletServers) { tsStrings = new ArrayList<>(tabletServers.size()); for (ServerInfo e : tabletServers.values()) { String flag = e.getUuid().equals(leaderUuid) ? "[L]" : ""; tsStrings.add(e.toString() + flag); } } // Sort so that we have a consistent iteration order regardless of // HashSet ordering. Collections.sort(tsStrings); sb.append(Joiner.on(',').join(tsStrings)); sb.append(']'); return sb.toString(); } /** * Removes the passed tablet server from this tablet's list of tablet servers. * @param uuid a tablet server to remove from this cache * @return true if this method removed ts from the list, else false */ boolean removeTabletClient(String uuid) { synchronized (tabletServers) { if (leaderUuid != null && leaderUuid.equals(uuid)) { leaderUuid = null; } // TODO(ghenke): Should this also remove the related replica? // As it stands there can be a replica with a missing tablet server. if (tabletServers.remove(uuid) != null) { return true; } LOG.debug("tablet {} already removed ts {}, size left is {}", getTabletId(), uuid, tabletServers.size()); return false; } } /** * Clears the leader UUID if the passed tablet server is the current leader. * If it is the current leader, then the next call to this tablet will have * to query the master to find the new leader. * @param uuid a tablet server that gave a sign that it isn't this tablet's leader */ void demoteLeader(String uuid) { synchronized (tabletServers) { if (leaderUuid == null) { LOG.debug("{} couldn't be demoted as the leader for {}, there is no known leader", uuid, getTabletId()); return; } if (leaderUuid.equals(uuid)) { leaderUuid = null; LOG.debug("{} was demoted as the leader for {}", uuid, getTabletId()); } else { LOG.debug("{} wasn't the leader for {}, current leader is {}", uuid, getTabletId(), leaderUuid); } } } /** * Get the information on the tablet server that we think holds the leader replica for this * tablet. * * @return information on a tablet server that we think has the leader, else null */ @Nullable ServerInfo getLeaderServerInfo() { synchronized (tabletServers) { return tabletServers.get(leaderUuid); } } /** * Get the information on the closest server. Servers are ranked from closest to furthest as * follows: * - Local servers * - Servers in the same location as the client * - All other servers * * @param location the location of the client * @return the information for a closest server, or null if this cache doesn't know any servers. */ @Nullable ServerInfo getClosestServerInfo(String location) { // This method returns // 1. a randomly picked server among local servers, if there is one based // on IP and assigned location, or // 2. a randomly picked server in the same assigned location, if there is a // server in the same location, or, finally, // 3. a randomly picked server among all tablet servers. // TODO(wdberkeley): Eventually, the client might use the hierarchical // structure of a location to determine proximity. // NOTE: this is the same logic implemented in client-internal.cc. synchronized (tabletServers) { if (tabletServers.isEmpty()) { return null; } ServerInfo result = null; List localServers = new ArrayList<>(); List serversInSameLocation = new ArrayList<>(); int randomIndex = RANDOM_SELECTION_INT % tabletServers.size(); int index = 0; for (ServerInfo e : tabletServers.values()) { boolean serverInSameLocation = !location.isEmpty() && e.inSameLocation(location); // Only consider a server "local" if we're in the same location, or if // there is missing location info. if (location.isEmpty() || e.getLocation().isEmpty() || serverInSameLocation) { if (e.isLocal()) { localServers.add(e); } } if (serverInSameLocation) { serversInSameLocation.add(e); } if (index == randomIndex) { result = e; } index++; } if (!localServers.isEmpty()) { randomIndex = RANDOM_SELECTION_INT % localServers.size(); return localServers.get(randomIndex); } if (!serversInSameLocation.isEmpty()) { randomIndex = RANDOM_SELECTION_INT % serversInSameLocation.size(); return serversInSameLocation.get(randomIndex); } return result; } } /** * Helper function to centralize the calling of methods based on the passed replica selection * mechanism. * * @param replicaSelection replica selection mechanism to use * @param location the location of the client * @return information on the server that matches the selection, can be null */ @Nullable ServerInfo getReplicaSelectedServerInfo(ReplicaSelection replicaSelection, String location) { switch (replicaSelection) { case LEADER_ONLY: return getLeaderServerInfo(); case CLOSEST_REPLICA: return getClosestServerInfo(location); default: throw new RuntimeException("unknown replica selection mechanism " + replicaSelection); } } /** * Get replicas of this tablet. The returned list may not be mutated. * * This list of replicas may include replicas for servers that have been * removed via `removeTabletClient`, therefore won't be returned via * `getTabletServersCopy`. * * @return the replicas of the tablet */ List getReplicas() { return replicas.get(); } public String getTableId() { return tableId; } String getTabletId() { return tabletId; } public Partition getPartition() { return partition; } byte[] getTabletIdAsBytes() { return tabletId.getBytes(UTF_8); } List getTabletServersCopy() { List results = new ArrayList<>(); synchronized (tabletServers) { results.addAll(tabletServers.values()); } return results; } @Override public int compareTo(RemoteTablet remoteTablet) { if (remoteTablet == null) { return 1; } return ComparisonChain.start() .compare(this.tableId, remoteTablet.tableId) .compare(this.partition, remoteTablet.partition).result(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof RemoteTablet)) { return false; } RemoteTablet that = (RemoteTablet) o; return this.compareTo(that) == 0; } @Override public int hashCode() { return Objects.hashCode(tableId, partition); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy