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

com.netflix.astyanax.connectionpool.impl.TokenPartitionedTopology Maven / Gradle / Ivy

There is a newer version: 3.10.2
Show newest version
package com.netflix.astyanax.connectionpool.impl;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.lang.StringUtils;
import org.cliffc.high_scale_lib.NonBlockingHashMap;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.netflix.astyanax.connectionpool.HostConnectionPool;
import com.netflix.astyanax.connectionpool.LatencyScoreStrategy;
import com.netflix.astyanax.connectionpool.TokenRange;
import com.netflix.astyanax.partitioner.Partitioner;

/**
 * Partition hosts by start token.  Each token may map to a list of partitions. The class maintains 3 different data structures to represent {@link HostConnectionPool}s
 * for a set of hosts in a partitioned ring. 
 * 
 * 
    *
  1. sortedRing - the list of all pools for all hosts sorted in order specified by the token ranges for the cluster *
  2. tokenToPartitionMap - map of token range to host connection pools for that range *
  3. allPools - all pools for all known hosts in case no tokens can be found by calls such as ring describe etc *
* * Prominent features of this class * *

* Maintaining the set of pools for all hosts in a token ring * See the {@link #setPools(Collection)} method for details on how the various data structures are updated from a list of {@link HostConnectionPool}s * Note that when host connection pools are set on this class, each host associated with the host conneciton pool can serve multiple partitions, * hence this class builds a reverse index from token range -> list < HostConnectionPool> using this update. *

* *

* Lookup all HostConnectionPools for a given row keyor token * See the {@link #getPartition(ByteBuffer)} method for details on how the various data structures are consulted for retrieving the comprehensive list of * host connection pools for a row key.
* Note that if the token range token is provided directly, then we can directly consult the tokenPartitionMap. But if we receive a row key, then that is converted * to an integer which is then used to do a bin search over the list of all sorted tokens in the token ring. *

* * * @author elandau * * @param * * @see {@link HostConnectionPoolPartition} for details of token ranges partition based connection pools maintained for each host. */ public class TokenPartitionedTopology implements Topology { /** * Sorted list of partitions. A binary search is performed on this list to determine * the list of hosts that own the token range. */ private AtomicReference>> sortedRing = new AtomicReference>>(new ArrayList>()); /** * Lookup of end token to partition */ private NonBlockingHashMap> tokenToPartitionMap = new NonBlockingHashMap>(); /** * Partition which contains all hosts. This is the fallback partition when no tokens are provided. */ private TokenHostConnectionPoolPartition allPools; /** * Strategy used to score hosts within a partition. */ private LatencyScoreStrategy strategy; /** * Assume random partitioner, for now */ private final Partitioner partitioner; /** * Comparator used to find the partition mapping to a token */ @SuppressWarnings("rawtypes") private Comparator tokenSearchComparator = new Comparator() { @SuppressWarnings("unchecked") @Override public int compare(Object arg0, Object arg1) { TokenHostConnectionPoolPartition partition = (TokenHostConnectionPoolPartition) arg0; BigInteger token = (BigInteger) arg1; return partition.id().compareTo(token); } }; /** * Compartor used to sort partitions in token order. */ @SuppressWarnings("rawtypes") private Comparator partitionComparator = new Comparator() { @SuppressWarnings("unchecked") @Override public int compare(Object arg0, Object arg1) { TokenHostConnectionPoolPartition partition0 = (TokenHostConnectionPoolPartition) arg0; TokenHostConnectionPoolPartition partition1 = (TokenHostConnectionPoolPartition) arg1; return partition0.id().compareTo(partition1.id()); } }; public TokenPartitionedTopology(Partitioner partitioner, LatencyScoreStrategy strategy) { this.strategy = strategy; this.partitioner = partitioner; this.allPools = new TokenHostConnectionPoolPartition(null, this.strategy); } protected TokenHostConnectionPoolPartition makePartition(BigInteger partition) { return new TokenHostConnectionPoolPartition(partition, strategy); } @SuppressWarnings("unchecked") @Override /** * Update the list of pools using the provided mapping of start token to collection of hosts * that own the token */ public synchronized boolean setPools(Collection> ring) { boolean didChange = false; Set> allPools = Sets.newHashSet(); // Create a mapping of end token to a list of hosts that own the token Map>> tokenHostMap = Maps.newHashMap(); for (HostConnectionPool pool : ring) { allPools.add(pool); if (!this.allPools.hasPool(pool)) didChange = true; for (TokenRange range : pool.getHost().getTokenRanges()) { BigInteger endToken = new BigInteger(range.getEndToken()); List> partition = tokenHostMap.get(endToken); if (partition == null) { partition = Lists.newArrayList(); tokenHostMap.put(endToken, partition); } partition.add(pool); } } // Temporary list of token that will be removed if not found in the new ring Set tokensToRemove = Sets.newHashSet(tokenHostMap.keySet()); // Iterate all tokens for (Entry>> entry : tokenHostMap.entrySet()) { BigInteger token = entry.getKey(); tokensToRemove.remove(token); // Add a new partition or modify an existing one TokenHostConnectionPoolPartition partition = tokenToPartitionMap.get(token); if (partition == null) { partition = makePartition(token); tokenToPartitionMap.put(token, partition); didChange = true; } if (partition.setPools(entry.getValue())) didChange = true; } // Remove the tokens that are no longer in the ring for (BigInteger token : tokensToRemove) { tokenHostMap.remove(token); didChange = true; } // Sort partitions by token if (didChange) { List> partitions = Lists.newArrayList(tokenToPartitionMap.values()); Collections.sort(partitions, partitionComparator); this.allPools.setPools(allPools); refresh(); this.sortedRing.set(Collections.unmodifiableList(partitions)); } return didChange; } @Override public synchronized void resumePool(HostConnectionPool pool) { refresh(); } @Override public synchronized void suspendPool(HostConnectionPool pool) { refresh(); } @Override public synchronized void refresh() { allPools.refresh(); for (TokenHostConnectionPoolPartition partition : tokenToPartitionMap.values()) { partition.refresh(); } } @Override public TokenHostConnectionPoolPartition getPartition(String token) { return tokenToPartitionMap.get(new BigInteger(token)); } @Override public TokenHostConnectionPoolPartition getPartition(ByteBuffer rowkey) { if (rowkey == null) return getAllPools(); BigInteger token = new BigInteger(partitioner.getTokenForKey(rowkey)); // First, get a copy of the partitions. List> partitions = this.sortedRing.get(); // Must have a token otherwise we default to the base class // implementation if (token == null || partitions == null || partitions.isEmpty()) { return getAllPools(); } // Do a binary search to find the token partition which owns the // token. We can get two responses here. // 1. The token is in the list in which case the response is the // index of the partition // 2. The token is not in the list in which case the response is the // index where the token would have been inserted into the list. // We convert this index (which is negative) to the index of the // previous position in the list. @SuppressWarnings("unchecked") int partitionIndex = Collections.binarySearch(partitions, token, tokenSearchComparator); if (partitionIndex < 0) { partitionIndex = -(partitionIndex + 1); } return partitions.get(partitionIndex % partitions.size()); } @Override public TokenHostConnectionPoolPartition getAllPools() { return allPools; } @Override public int getPartitionCount() { return tokenToPartitionMap.size(); } @Override public synchronized void removePool(HostConnectionPool pool) { allPools.removePool(pool); for (TokenHostConnectionPoolPartition partition : tokenToPartitionMap.values()) { partition.removePool(pool); } } @Override public synchronized void addPool(HostConnectionPool pool) { allPools.addPool(pool); } @Override public List getPartitionNames() { return Lists.newArrayList(Collections2.transform(tokenToPartitionMap.keySet(), new Function() { @Override public String apply(BigInteger input) { return input.toString(); } })); } @Override public Map> getPartitions() { Map> result = Maps.newHashMap(); for (Entry> entry : tokenToPartitionMap.entrySet()) { result.put(entry.getKey().toString(), entry.getValue()); } return result; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("TokenPartitionTopology["); sb.append(StringUtils.join(Lists.transform(this.sortedRing.get(), new Function, String>() { @Override public String apply(TokenHostConnectionPoolPartition input) { return input.id().toString() + "\n"; } }), ",")); sb.append("]"); return sb.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy