void routerMetadataCacheErrorOnGetCompositeBlob(ServerErrorCode serverErrorCode, RouterErrorCode routerErrorCode,
      boolean deleteCacheEntryOnFail) 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);

      // successful get
      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));

      // failed get
      mockServerLayout.getMockServers().forEach(mockServer -> mockServer.setServerErrorForAllRequests(serverErrorCode));
      TestCallback<GetBlobResult> testCallback = new TestCallback<>();
      Future<GetBlobResult> future =
          router.getBlob(blobId, new GetBlobOptionsBuilder().range(range).build(), testCallback, null);
      assertFailureAndCheckErrorCode(future, testCallback, routerErrorCode);
      assertTrue("Must encounter a cache hit",
          ambryCacheWithStats.getCacheHitCountDown().await(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
      if (deleteCacheEntryOnFail) {
        assertTrue("Must delete metadata in cache",
            ambryCacheWithStats.getDeleteObjectCountDown().await(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS));
        assertNull("Blob metadata must be absent in metadata cache", router.getBlobMetadataCache().getObject(blobId));
      } else {
        assertNotNull("Blob metadata must be present in metadata cache",
            router.getBlobMetadataCache().getObject(blobId));
      }
      mockServerLayout.getMockServers().forEach(mockServer -> mockServer.resetServerErrors());
      // There should be flag to disable this cache !
      router.getNotFoundCache().invalidateAll();

      // successful get, cache must repopulate
      router.getBlob(blobId, new GetBlobOptionsBuilder().range(range).build())
          .get(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