@Override
  public <KEY> URIMappingResult<KEY> mapUris(List<URIKeyPair<KEY>> requestUriKeyPairs)
      throws ServiceUnavailableException
  {
    if (requestUriKeyPairs == null || requestUriKeyPairs.isEmpty())
    {
      return new URIMappingResult<>(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap());
    }

    // API assumes that all requests will be made to the same service, just use the first request to get the service name and act as sample uri
    URI sampleURI = requestUriKeyPairs.get(0).getRequestUri();
    String serviceName = LoadBalancerUtil.getServiceNameFromUri(sampleURI);

    // To achieve scatter-gather, we require the following information
    PartitionAccessor accessor = _partitionInfoProvider.getPartitionAccessor(serviceName);
    Map<Integer, Ring<URI>> rings = _hashRingProvider.getRings(sampleURI);
    HashFunction<Request> hashFunction = _hashRingProvider.getRequestHashFunction(serviceName);

    Map<Integer, Set<KEY>> unmapped = new HashMap<>();

    // Pass One
    Map<Integer, List<URIKeyPair<KEY>>> requestsByPartition =
        distributeToPartitions(requestUriKeyPairs, accessor, unmapped);

    // Pass Two
    Map<URI, Integer> hostToParitionId = new HashMap<>();
    Map<URI, Set<KEY>> hostToKeySet = distributeToHosts(requestsByPartition, rings, hashFunction, hostToParitionId, unmapped);

    return new URIMappingResult<>(hostToKeySet, unmapped, hostToParitionId);
  }
@Test
  public void testMapUrisStickyRoutingOnly() throws ServiceUnavailableException, PartitionAccessException
  {
    int partitionCount = 1;
    int requestPerPartition = 1000;
    int totalHostCount = 100;

    HashRingProvider ringProvider = createStaticHashRingProvider(totalHostCount, partitionCount, getHashFunction(true));
    PartitionInfoProvider infoProvider = createRangeBasedPartitionInfoProvider(partitionCount);
    URIMapper mapper = new RingBasedUriMapper(ringProvider, infoProvider);

    List<URIKeyPair<Integer>> requests = testUtil.generateRequests(partitionCount, requestPerPartition);

    URIMappingResult<Integer> results1 = mapper.mapUris(requests);
    URIMappingResult<Integer> results2 = mapper.mapUris(requests);

    // Sticky routing between two runs
    Assert.assertEquals(results1.getMappedKeys(), results2.getMappedKeys());
    Assert.assertEquals(results1.getUnmappedKeys(), results2.getUnmappedKeys());

    Map<URI, Set<Integer>> mapping = results1.getMappedKeys();

    // Testing universal stickiness, take out 50 requests randomly and make sure they would be resolved to the same host as does URIMapper
    Collections.shuffle(requests);
    HashFunction<Request> hashFunction = ringProvider.getRequestHashFunction(TEST_SERVICE);
    for (int i = 0; i < 50; i++)
    {
      URIKeyPair<Integer> request = requests.get(i);
      int partitionId = infoProvider.getPartitionAccessor(TEST_SERVICE).getPartitionId(request.getRequestUri());
      Ring<URI> ring = ringProvider.getRings(request.getRequestUri()).get(partitionId);
      URI uri = ring.get(hashFunction.hash(new URIRequest(request.getRequestUri())));
      Assert.assertTrue(mapping.keySet().contains(uri));
    }

    // Only one partition
    Assert.assertEquals(1, new HashSet<>(results1.getHostPartitionInfo().values()).size());
    Assert.assertEquals(1, new HashSet<>(results2.getHostPartitionInfo().values()).size());
  }