/* * * 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.cassandra.locator; import com.google.common.collect.Iterators; import org.apache.cassandra.dht.Range; import org.apache.cassandra.dht.Token; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.net.InetAddress; import java.util.*; public class PendingRangeMaps implements Iterable<Map.Entry<Range<Token>, List<InetAddress>>> { private static final Logger logger = LoggerFactory.getLogger(PendingRangeMaps.class); /** * We have for NavigableMap to be able to search for ranges containing a token efficiently. * * First two are for non-wrap-around ranges, and the last two are for wrap-around ranges. */ // ascendingMap will sort the ranges by the ascending order of right token final NavigableMap<Range<Token>, List<InetAddress>> ascendingMap; /** * sorting end ascending, if ends are same, sorting begin descending, so that token (end, end) will * come before (begin, end] with the same end, and (begin, end) will be selected in the tailMap. */ static final Comparator<Range<Token>> ascendingComparator = new Comparator<Range<Token>>() { @Override public int compare(Range<Token> o1, Range<Token> o2) { int res = o1.right.compareTo(o2.right); if (res != 0) return res; return o2.left.compareTo(o1.left); } }; // ascendingMap will sort the ranges by the descending order of left token final NavigableMap<Range<Token>, List<InetAddress>> descendingMap; /** * sorting begin descending, if begins are same, sorting end descending, so that token (begin, begin) will * come after (begin, end] with the same begin, and (begin, end) won't be selected in the tailMap. */ static final Comparator<Range<Token>> descendingComparator = new Comparator<Range<Token>>() { @Override public int compare(Range<Token> o1, Range<Token> o2) { int res = o2.left.compareTo(o1.left); if (res != 0) return res; // if left tokens are same, sort by the descending of the right tokens. return o2.right.compareTo(o1.right); } }; // these two maps are for warp around ranges. final NavigableMap<Range<Token>, List<InetAddress>> ascendingMapForWrapAround; /** * for wrap around range (begin, end], which begin > end. * Sorting end ascending, if ends are same, sorting begin ascending, * so that token (end, end) will come before (begin, end] with the same end, and (begin, end] will be selected in * the tailMap. */ static final Comparator<Range<Token>> ascendingComparatorForWrapAround = new Comparator<Range<Token>>() { @Override public int compare(Range<Token> o1, Range<Token> o2) { int res = o1.right.compareTo(o2.right); if (res != 0) return res; return o1.left.compareTo(o2.left); } }; final NavigableMap<Range<Token>, List<InetAddress>> descendingMapForWrapAround; /** * for wrap around ranges, which begin > end. * Sorting end ascending, so that token (begin, begin) will come after (begin, end] with the same begin, * and (begin, end) won't be selected in the tailMap. */ static final Comparator<Range<Token>> descendingComparatorForWrapAround = new Comparator<Range<Token>>() { @Override public int compare(Range<Token> o1, Range<Token> o2) { int res = o2.left.compareTo(o1.left); if (res != 0) return res; return o1.right.compareTo(o2.right); } }; public PendingRangeMaps() { this.ascendingMap = new TreeMap<Range<Token>, List<InetAddress>>(ascendingComparator); this.descendingMap = new TreeMap<Range<Token>, List<InetAddress>>(descendingComparator); this.ascendingMapForWrapAround = new TreeMap<Range<Token>, List<InetAddress>>(ascendingComparatorForWrapAround); this.descendingMapForWrapAround = new TreeMap<Range<Token>, List<InetAddress>>(descendingComparatorForWrapAround); } static final void addToMap(Range<Token> range, InetAddress address, NavigableMap<Range<Token>, List<InetAddress>> ascendingMap, NavigableMap<Range<Token>, List<InetAddress>> descendingMap) { List<InetAddress> addresses = ascendingMap.get(range); if (addresses == null) { addresses = new ArrayList<InetAddress>(1); ascendingMap.put(range, addresses); descendingMap.put(range, addresses); } addresses.add(address); } public void addPendingRange(Range<Token> range, InetAddress address) { if (Range.isWrapAround(range.left, range.right)) { addToMap(range, address, ascendingMapForWrapAround, descendingMapForWrapAround); } else { addToMap(range, address, ascendingMap, descendingMap); } } static final void addIntersections(Set<InetAddress> endpointsToAdd, NavigableMap<Range<Token>, List<InetAddress>> smallerMap, NavigableMap<Range<Token>, List<InetAddress>> biggerMap) { // find the intersection of two sets for (Range<Token> range : smallerMap.keySet()) { List<InetAddress> addresses = biggerMap.get(range); if (addresses != null) { endpointsToAdd.addAll(addresses); } } } public Collection<InetAddress> pendingEndpointsFor(Token token) { Set<InetAddress> endpoints = new HashSet<>(); Range searchRange = new Range(token, token); // search for non-wrap-around maps NavigableMap<Range<Token>, List<InetAddress>> ascendingTailMap = ascendingMap.tailMap(searchRange, true); NavigableMap<Range<Token>, List<InetAddress>> descendingTailMap = descendingMap.tailMap(searchRange, false); // add intersections of two maps if (ascendingTailMap.size() < descendingTailMap.size()) { addIntersections(endpoints, ascendingTailMap, descendingTailMap); } else { addIntersections(endpoints, descendingTailMap, ascendingTailMap); } // search for wrap-around sets ascendingTailMap = ascendingMapForWrapAround.tailMap(searchRange, true); descendingTailMap = descendingMapForWrapAround.tailMap(searchRange, false); // add them since they are all necessary. for (Map.Entry<Range<Token>, List<InetAddress>> entry : ascendingTailMap.entrySet()) { endpoints.addAll(entry.getValue()); } for (Map.Entry<Range<Token>, List<InetAddress>> entry : descendingTailMap.entrySet()) { endpoints.addAll(entry.getValue()); } return endpoints; } public String printPendingRanges() { StringBuilder sb = new StringBuilder(); for (Map.Entry<Range<Token>, List<InetAddress>> entry : this) { Range<Token> range = entry.getKey(); for (InetAddress address : entry.getValue()) { sb.append(address).append(':').append(range); sb.append(System.getProperty("line.separator")); } } return sb.toString(); } @Override public Iterator<Map.Entry<Range<Token>, List<InetAddress>>> iterator() { return Iterators.concat(ascendingMap.entrySet().iterator(), ascendingMapForWrapAround.entrySet().iterator()); } }