@Test
  public void originDcNotFoundTerminateEarlyTest() {
    initialize();
    originatingDcName = datanodes.get(datanodes.size() - 1).getDatacenterName();

    // Case 1: Check all replicas in originating DC before returning that blob is not found.
    Properties props = new Properties();
    props.setProperty(RouterConfig.ROUTER_GET_SUCCESS_TARGET, Integer.toString(2));
    props.setProperty(RouterConfig.ROUTER_GET_REQUEST_PARALLELISM, Integer.toString(3));
    props.setProperty(RouterConfig.ROUTER_OPERATION_TRACKER_CHECK_ALL_ORIGINATING_REPLICAS_FOR_NOT_FOUND,
        Boolean.toString(true));
    OperationTracker ot = getOperationTracker(props, true, 3, RouterOperation.GetBlobOperation, true, true);
    sendRequests(ot, 3, false);
    assertEquals("Should have 3 replicas", 3, inflightReplicas.size());
    for (int i = 0; i < 3; i++) {
      ReplicaId replica = inflightReplicas.poll();
      // fail first 3 requests to local replicas
      ot.onResponse(replica, TrackedRequestFinalState.NOT_FOUND);
    }
    assertFalse("Operation should have not succeeded", ot.hasSucceeded());
    assertFalse("Operation should have not failed on NOT_FOUND", ot.hasFailedOnNotFound());
    assertFalse("Operation should not be done", ot.isDone());

    sendRequests(ot, 3, false);
    assertEquals("Should have 3 replicas", 3, inflightReplicas.size());
    // Send three not found response from originating dc, it will terminate the operation.
    for (int i = 0; i < 3; i++) {
      ReplicaId replica = inflightReplicas.poll();
      // fail first 3 requests to originating dc replicas
      ot.onResponse(replica, TrackedRequestFinalState.NOT_FOUND);
      assertEquals("Should be originatingDcName DC", originatingDcName, replica.getDataNodeId().getDatacenterName());
      if (i < 2) {
        assertFalse("Operation should not have failed on NOT_FOUND", ot.hasFailedOnNotFound());
      }
    }
    assertTrue("Operation should have failed on NOT_FOUND", ot.hasFailedOnNotFound());
    assertTrue("Operation should be done", ot.isDone());

    // Case 2: Check minimum needed number of replicas in originating DC before returning that blob is not found
    repetitionTracker.clear();
    inflightReplicas.clear();
    props.setProperty(RouterConfig.ROUTER_OPERATION_TRACKER_CHECK_ALL_ORIGINATING_REPLICAS_FOR_NOT_FOUND,
        Boolean.toString(false));
    ot = getOperationTracker(props, true, 3, RouterOperation.GetBlobOperation, true, true);
    sendRequests(ot, 3, false);
    assertEquals("Should have 3 replicas", 3, inflightReplicas.size());
    for (int i = 0; i < 3; i++) {
      ReplicaId replica = inflightReplicas.poll();
      // fail first 3 requests to local replicas
      ot.onResponse(replica, TrackedRequestFinalState.NOT_FOUND);
    }
    assertFalse("Operation should have not succeeded", ot.hasSucceeded());
    assertFalse("Operation should have not failed on NOT_FOUND", ot.hasFailedOnNotFound());
    assertFalse("Operation should not be done", ot.isDone());

    sendRequests(ot, 3, false);
    assertEquals("Should have 3 replicas", 3, inflightReplicas.size());
    // Send two not found response from originating dc, it will terminate the operation.
    for (int i = 0; i < 2; i++) {
      ReplicaId replica = inflightReplicas.poll();
      // fail first 2 requests to originating dc replicas
      ot.onResponse(replica, TrackedRequestFinalState.NOT_FOUND);
      assertEquals("Should be originatingDcName DC", originatingDcName, replica.getDataNodeId().getDatacenterName());
      if (i < 1) {
        assertFalse("Operation should not have failed on NOT_FOUND", ot.hasFailedOnNotFound());
      }
    }
    assertTrue("Operation should have failed on NOT_FOUND", ot.hasFailedOnNotFound());
    assertTrue("Operation should be done", ot.isDone());
  }
Test the case when NotFound Error triggered.
ambry-router/src/test/java/com/github/ambry/router/OperationTrackerTest.java