/** * 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 com.facebook.infrastructure.dht; import com.facebook.infrastructure.locator.TokenMetadata; import com.facebook.infrastructure.net.EndPoint; import com.facebook.infrastructure.service.StorageService; import com.facebook.infrastructure.utils.LogUtil; import org.apache.log4j.Logger; import java.math.BigInteger; import java.util.*; /** * This class handles the boostrapping responsibilities for * any new endpoint. */ public class BootStrapper implements Runnable { private static Logger logger_ = Logger.getLogger(BootStrapper.class); /* endpoints that need to be bootstrapped */ protected EndPoint[] targets_ = new EndPoint[0]; /* tokens of the nodes being bootstapped. */ protected BigInteger[] tokens_ = new BigInteger[0]; protected TokenMetadata tokenMetadata_ = null; private List<EndPoint> filters_ = new ArrayList<EndPoint>(); public BootStrapper(EndPoint[] target, BigInteger[] token) { targets_ = target; tokens_ = token; tokenMetadata_ = StorageService.instance().getTokenMetadata(); } public BootStrapper(EndPoint[] target, BigInteger[] token, EndPoint[] filters) { this(target, token); Collections.addAll(filters_, filters); } public void run() { try { logger_.debug("Beginning bootstrap process for " + targets_ + " ..."); /* copy the token to endpoint map */ Map<BigInteger, EndPoint> tokenToEndPointMap = tokenMetadata_.cloneTokenEndPointMap(); /* remove the tokens associated with the endpoints being bootstrapped */ for ( BigInteger token : tokens_ ) { tokenToEndPointMap.remove(token); } Set<BigInteger> oldTokens = new HashSet<BigInteger>( tokenToEndPointMap.keySet() ); Range[] oldRanges = StorageService.instance().getAllRanges(oldTokens); logger_.debug("Total number of old ranges " + oldRanges.length); /* * Find the ranges that are split. Maintain a mapping between * the range being split and the list of subranges. */ Map<Range, List<Range>> splitRanges = LeaveJoinProtocolHelper.getRangeSplitRangeMapping(oldRanges, tokens_); /* Calculate the list of nodes that handle the old ranges */ Map<Range, List<EndPoint>> oldRangeToEndPointMap = StorageService.instance().constructRangeToEndPointMap(oldRanges, tokenToEndPointMap); /* Mapping of split ranges to the list of endpoints responsible for the range */ Map<Range, List<EndPoint>> replicasForSplitRanges = new HashMap<Range, List<EndPoint>>(); Set<Range> rangesSplit = splitRanges.keySet(); for ( Range splitRange : rangesSplit ) { replicasForSplitRanges.put( splitRange, oldRangeToEndPointMap.get(splitRange) ); } /* Remove the ranges that are split. */ for ( Range splitRange : rangesSplit ) { oldRangeToEndPointMap.remove(splitRange); } /* Add the subranges of the split range to the map with the same replica set. */ for ( Range splitRange : rangesSplit ) { List<Range> subRanges = splitRanges.get(splitRange); List<EndPoint> replicas = replicasForSplitRanges.get(splitRange); for ( Range subRange : subRanges ) { /* Make sure we clone or else we are hammered. */ oldRangeToEndPointMap.put(subRange, new ArrayList<EndPoint>(replicas)); } } /* Add the new token and re-calculate the range assignments */ Collections.addAll( oldTokens, tokens_ ); Range[] newRanges = StorageService.instance().getAllRanges(oldTokens); logger_.debug("Total number of new ranges " + newRanges.length); /* Calculate the list of nodes that handle the new ranges */ Map<Range, List<EndPoint>> newRangeToEndPointMap = StorageService.instance().constructRangeToEndPointMap(newRanges); /* Calculate ranges that need to be sent and from whom to where */ Map<Range, List<BootstrapSourceTarget>> rangesWithSourceTarget = LeaveJoinProtocolHelper.getRangeSourceTargetInfo(oldRangeToEndPointMap, newRangeToEndPointMap); /* Send messages to respective folks to stream data over to the new nodes being bootstrapped */ LeaveJoinProtocolHelper.assignWork(rangesWithSourceTarget, filters_); } catch ( Throwable th ) { logger_.debug( LogUtil.throwableToString(th) ); } } private Range getMyOldRange() { Map<EndPoint, BigInteger> oldEndPointToTokenMap = tokenMetadata_.cloneEndPointTokenMap(); Map<BigInteger, EndPoint> oldTokenToEndPointMap = tokenMetadata_.cloneTokenEndPointMap(); for (EndPoint endpoint : targets_) { oldEndPointToTokenMap.remove(endpoint); } for (BigInteger token : tokens_) { oldTokenToEndPointMap.remove(token); } BigInteger myToken = oldEndPointToTokenMap.get(StorageService.getLocalStorageEndPoint()); List<BigInteger> allTokens = new ArrayList<BigInteger>(oldTokenToEndPointMap.keySet()); Collections.sort(allTokens); int index = Collections.binarySearch(allTokens, myToken); /* Calculate the lhs for the range */ BigInteger lhs = (index == 0) ? allTokens.get(allTokens.size() - 1) : allTokens.get( index - 1); return new Range( lhs, myToken ); } }