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

com.netflix.astyanax.cql.CqlRingDescriber Maven / Gradle / Ivy

package com.netflix.astyanax.cql;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.netflix.astyanax.connectionpool.TokenRange;
import com.netflix.astyanax.connectionpool.impl.TokenRangeImpl;
import com.netflix.astyanax.partitioner.Murmur3Partitioner;

/**
 * Helper class that parses the ring information from the system and peers table. 
 * Note that it maintains a cached reference and allows the user to either reuse the cache or refresh the cahe.
 *  
 * @author poberai
 */
public class CqlRingDescriber {

	private final AtomicReference>  cachedReference = new AtomicReference>(null);
	
	private static CqlRingDescriber Instance = new CqlRingDescriber();
	
	private CqlRingDescriber() {
	}
	
	public static CqlRingDescriber getInstance() {
		return Instance; 
	}
	
	public List getTokenRanges(Session session, boolean cached) {
		
		if (cached && cachedReference.get() != null) {
			return cachedReference.get();
		}
		
		// else get the actual token range list and then cache it
		List ranges = getTokenRanges(session, null, null);
		cachedReference.set(ranges);
		return ranges;
	}
	
	public List getTokenRanges(Session session, String dc, String rack) {
		
		List hosts = new ArrayList();

		Statement query = QueryBuilder.select().all().from("system", "local"); 
		ResultSet resultSet = session.execute(query);
		hosts.add(new HostInfo(resultSet.one(), resultSet));
		
		query = QueryBuilder.select("peer", "data_center", "host_id", "rack", "tokens").from("system", "peers"); 
		resultSet = session.execute(query);
		for (Row row : resultSet.all()) {
			hosts.add(new HostInfo(row, null));
		}
		
		Collections.sort(hosts);

		List ranges = new ArrayList();

		for (int index = 0; index endpoints = new ArrayList();

			if (matchNode(dc, rack, thisNode)) {
				endpoints.add(thisNode.endpoint); // the primary range owner
			}

			// secondary node
			int nextIndex = getNextIndex(index, hosts.size());
			if (nextIndex != index) {

				HostInfo nextNode = hosts.get(nextIndex);
				if (matchNode(dc, rack, nextNode)) {
					endpoints.add(nextNode.endpoint);
				}

				// tertiary node
				nextIndex = getNextIndex(nextIndex, hosts.size());
				nextNode = hosts.get(nextIndex);
				if (matchNode(dc, rack, nextNode)) {
					endpoints.add(nextNode.endpoint);
				}
			}
			int prevIndex = getPrevIndex(index, hosts.size());
			String startToken = hosts.get(prevIndex).token.toString();
			String endToken = thisNode.token.toString();

			if (startToken.equals(endToken)) {
				// TOKENS are the same. This happens during testing. 
				startToken = Murmur3Partitioner.get().getMinToken();
				endToken = Murmur3Partitioner.get().getMinToken();
				
			}
			ranges.add(new TokenRangeImpl(startToken, endToken, endpoints));
		}

		return ranges;
	}
	
	private boolean matchNode(String dc, String rack, HostInfo host) {
		
		if (dc == null && rack == null) {
			return true; // node matches since there is no filter
		}
		
		if (dc != null && !dc.equals(host.datacenter)) {
			return false; // wrong dc 
		}
		
		if (rack != null && !rack.equals(host.rack)) {
			return false; // wrong rack
		}
		
		return true; // match!
	}
	
	private int getNextIndex(int i, int n) {
		
		int next = ++i;
		if (i >= n) {
			return 0; 
		} else {
			return next;
		}
	}
	
	private int getPrevIndex(int i, int n) {
		
		int prev = --i;
		if (i < 0) {
			return n-1; 
		} else {
			return prev;
		}
	}

	private class HostInfo implements Comparable {
		
		private final BigInteger token;
		private final String endpoint; 
		private final UUID hostId; 
		private final String datacenter;
		private final String rack; 
		
		private HostInfo(Row row, ResultSet rs) {
			
			if (row == null) {
				throw new RuntimeException("RS is empty for system.local query");
			}
		
			Set tokens = row.getSet("tokens", String.class);
			
			String theToken = tokens.iterator().next();
			token = new BigInteger(theToken);
			
			hostId = row.getUUID("host_id");
			datacenter = row.getString("data_center");
			rack = row.getString("rack");

			if (rs != null) {
				endpoint = rs.getExecutionInfo().getQueriedHost().getAddress().getHostAddress();
			} else {
				endpoint = row.getInet("peer").getHostAddress();
			}
		}

		@Override
		public int hashCode() {
			final int prime = 31;
			int result = 1;
			result = prime * result + ((token == null) ? 0 : token.hashCode());
			result = prime * result + ((endpoint == null) ? 0 : endpoint.hashCode());
			result = prime * result + ((hostId == null) ? 0 : hostId.hashCode());
			result = prime * result + ((datacenter == null) ? 0 : datacenter.hashCode());
			result = prime * result + ((rack == null) ? 0 : rack.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;
			
			HostInfo other = (HostInfo) obj;
			boolean equal = true; 
			
			equal &= (token != null) ? token.equals(other.token) : (other.token == null);
			equal &= (endpoint != null) ? endpoint.equals(other.endpoint) : (other.endpoint == null);
			equal &= (hostId != null) ? hostId.equals(other.hostId) : (other.hostId == null);
			equal &= (datacenter != null) ? datacenter.equals(other.datacenter) : (other.datacenter == null);
			equal &= (rack != null) ? rack.equals(other.rack) : (other.rack == null);
			
			return equal;
		}

		@Override
		public String toString() {
			return "HostInfo [token=" + token + ", endpoint=" + endpoint
					+ ", hostId=" + hostId.toString() + ", datacenter=" + datacenter
					+ ", rack=" + rack + "]";
		}

		@Override
		public int compareTo(HostInfo o) {
			return this.token.compareTo(o.token);
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy