@Test
  public void testRouterMetadataCacheUpdateTTLCompositeBlob() throws Exception {
    try {
      Properties properties = getNonBlockingRouterProperties(localDcName);
      properties.setProperty("router.blob.metadata.cache.enabled", Boolean.toString(true));
      properties.setProperty("router.smallest.blob.for.metadata.cache", Long.toString(0));
      setRouterWithMetadataCache(properties, new AmbryCacheStats(1, 1, 1, 1));
      AmbryCacheWithStats ambryCacheWithStats = (AmbryCacheWithStats) router.getBlobMetadataCache();
      assertNotNull("Metadata cache must be created", ambryCacheWithStats);

      // put a composite blob and test it is absent in the metadata cache
      setOperationParams(2 * PUT_CONTENT_SIZE, TTL_SECS);
      String blobId =
          router.putBlob(putBlobProperties, putUserMetadata, putChannel, new PutBlobOptionsBuilder().build())
              .get(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
      assertTtl(router, Collections.singleton(blobId), TTL_SECS);

      // get one byte from composite blob, and it's metadata must be present in cache
      ByteRange range = ByteRanges.fromOffsetRange(1, 2);
      router.getBlob(blobId, new GetBlobOptionsBuilder().range(range).build())
          .get(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
      assertTrue("Must encounter a cache miss",
          ambryCacheWithStats.getCacheMissCountDown().await(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
      assertTrue("Must put metadata in cache",
          ambryCacheWithStats.getPutObjectCountDown().await(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
      assertNotNull("Blob metadata must be present in metadata cache", router.getBlobMetadataCache().getObject(blobId));

      // update TTL and make it permanent
      router.updateBlobTtl(blobId, null, Utils.Infinite_Time).get(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
      assertTtl(router, Collections.singleton(blobId), Utils.Infinite_Time);

      // get one byte from composite blob, and it's metadata must be present in cache
      router.getBlob(blobId, new GetBlobOptionsBuilder().range(range).build())
          .get(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
      assertTrue("Must encounter a cache hit",
          ambryCacheWithStats.getCacheHitCountDown().await(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
      assertNotNull("Blob metadata must be present in metadata cache", router.getBlobMetadataCache().getObject(blobId));
    } finally {
      router.close();
    }
  }
Tests that there no metadata cache evictions with a large enough cache.
@throws Exception
ambry-router/src/test/java/com/github/ambry/router/NonBlockingRouterTest.java