| /* |
| * |
| * 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()); |
| } |
| } |