public Pair<BlobId, DataNodeId> blobTtlUpdate503AndThenFixedByODROrOffline(boolean enableODR,
      boolean enableOfflineRepair) throws Exception {
    String updateServiceId = "update-service";
    MockServerLayout layout = new MockServerLayout(mockClusterMap);
    setRouter(getNonBlockingRouterPropertiesForRepairRequests(localDcName, enableODR, enableOfflineRepair, false),
        layout, new LoggingNotificationSystem());
    setOperationParams();
    String blobId = router.putBlob(putBlobProperties, putUserMetadata, putChannel, new PutBlobOptionsBuilder().build())
        .get(AWAIT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
    assertTtl(router, Collections.singleton(blobId), TimeUnit.DAYS.toSeconds(7));

    // set two replicas Replica_Unavailable.
    DataNodeId sourceDataNode = null;
    for (MockServer server : layout.getMockServers()) {
      if (server.getDataCenter().equals(localDcName)) {
        // local DC, return NO_ERROR for one replica,
        if (sourceDataNode == null) {
          sourceDataNode = mockClusterMap.getDataNodeId(server.getHostName(), server.getHostPort());
        } else {
          server.setServerErrorForAllRequests(ServerErrorCode.ReplicaUnavailable);
        }
      }
    }

    try {
      router.updateBlobTtl(blobId, updateServiceId, Utils.Infinite_Time).get();
      if (!enableODR && !enableOfflineRepair) {
        fail("Should fail with un-available if ODR and offline repair are disabled.");
      }
    } catch (Exception e) {
      if (!enableODR && !enableOfflineRepair) {
        RouterException r = (RouterException) e.getCause();
        Assert.assertEquals(RouterErrorCode.AmbryUnavailable, r.getErrorCode());
      } else {
        fail("Should be successful when ODR or offline repair is enabled.");
      }
    }

    // simulate Replica_Unavailable for all local replicas. Then verify ttl from the remote replica
    for (MockServer server : layout.getMockServers()) {
      if (server.getDataCenter().equals(localDcName)) {
        server.setServerErrorForAllRequests(ServerErrorCode.ReplicaUnavailable);
      }
    }
    try {
      router.getBlob(blobId, new GetBlobOptionsBuilder().build(), null, null).get();
      if (enableODR) {
        assertTtl(router, Collections.singleton(blobId), Utils.Infinite_Time);
      } else {
        fail("Should return AmbryUnavailable if ODR is not enabled.");
      }
    } catch (Exception e) {
      RouterException r = (RouterException) e.getCause();
      if (!enableODR) {
        Assert.assertEquals("GetBlob error is not expected", RouterErrorCode.AmbryUnavailable, r.getErrorCode());
      } else {
        fail("Shouldn't hit exception when ODR is enabled.");
      }
    } finally {
      layout.getMockServers().forEach(mockServer -> mockServer.setServerErrorForAllRequests(null));
      router.getNotFoundCache().invalidateAll();
      if (router != null) {
        router.close();
      }
    }

    return new Pair<>(RouterUtils.getBlobIdFromString(blobId, mockClusterMap), sourceDataNode);
  }
TtlUpdate first hits 503. Then it's either fixed by ODR or by offline repair.
@param enableODR enable On demand replication
@param enableOfflineRepair enable the offline repair
@return a pair of the blob id and source replica which returns the success status.
@throws Exception
ambry-router/src/test/java/com/github/ambry/router/NonBlockingRouterTest.java