/**
   * Update token map with a set of token/endpoint pairs in normal state.
   *
   * <p>Prefer this whenever there are multiple pairs to update, as each update (whether a single or
   * multiple) is expensive (CASSANDRA-3831).
   *
   * @param endpointTokens
   */
  public void updateNormalTokens(Multimap<InetAddress, Token> endpointTokens) {
    if (endpointTokens.isEmpty()) return;

    lock.writeLock().lock();
    try {
      boolean shouldSortTokens = false;
      for (InetAddress endpoint : endpointTokens.keySet()) {
        Collection<Token> tokens = endpointTokens.get(endpoint);

        assert tokens != null && !tokens.isEmpty();

        bootstrapTokens.removeValue(endpoint);
        tokenToEndpointMap.removeValue(endpoint);
        topology.addEndpoint(endpoint);
        leavingEndpoints.remove(endpoint);
        removeFromMoving(endpoint); // also removing this endpoint from moving

        for (Token token : tokens) {
          InetAddress prev = tokenToEndpointMap.put(token, endpoint);
          if (!endpoint.equals(prev)) {
            if (prev != null)
              logger.warn("Token {} changing ownership from {} to {}", token, prev, endpoint);
            shouldSortTokens = true;
          }
        }
      }

      if (shouldSortTokens) sortedTokens = sortTokens();
    } finally {
      lock.writeLock().unlock();
    }
  }
  public void removeEndpoint(InetAddress endpoint) {
    assert endpoint != null;

    lock.writeLock().lock();
    try {
      bootstrapTokens.removeValue(endpoint);
      tokenToEndpointMap.removeValue(endpoint);
      topology.removeEndpoint(endpoint);
      leavingEndpoints.remove(endpoint);
      endpointToHostIdMap.remove(endpoint);
      sortedTokens = sortTokens();
      invalidateCachedRings();
    } finally {
      lock.writeLock().unlock();
    }
  }
  public void addBootstrapTokens(Collection<Token> tokens, InetAddress endpoint) {
    assert tokens != null && !tokens.isEmpty();
    assert endpoint != null;

    lock.writeLock().lock();
    try {

      InetAddress oldEndpoint;

      for (Token token : tokens) {
        oldEndpoint = bootstrapTokens.get(token);
        if (oldEndpoint != null && !oldEndpoint.equals(endpoint))
          throw new RuntimeException(
              "Bootstrap Token collision between "
                  + oldEndpoint
                  + " and "
                  + endpoint
                  + " (token "
                  + token);

        oldEndpoint = tokenToEndpointMap.get(token);
        if (oldEndpoint != null && !oldEndpoint.equals(endpoint))
          throw new RuntimeException(
              "Bootstrap Token collision between "
                  + oldEndpoint
                  + " and "
                  + endpoint
                  + " (token "
                  + token);
      }

      bootstrapTokens.removeValue(endpoint);

      for (Token token : tokens) bootstrapTokens.put(token, endpoint);
    } finally {
      lock.writeLock().unlock();
    }
  }