@VisibleForTesting
  void mutateRowWithHardTimeout(RowMutation rowMutation) {
    ApiFuture<Void> mutateRowFuture = dataClient.mutateRowAsync(rowMutation);
    try {
      mutateRowFuture.get(
          BigtableChangeStreamAccessor.MUTATE_ROW_DEADLINE.getSeconds() + 10, TimeUnit.SECONDS);
    } catch (TimeoutException timeoutException) {
      mutateRowFuture.cancel(true);
      throw new RuntimeException(
          "Cancelled mutateRow request after exceeding deadline", timeoutException);
    } catch (ExecutionException executionException) {
      if (executionException.getCause() instanceof RuntimeException) {
        throw (RuntimeException) executionException.getCause();
      }
      throw new RuntimeException(executionException);
    } catch (InterruptedException interruptedException) {
      Thread.currentThread().interrupt();
      throw new RuntimeException(interruptedException);
    }
  }
@Test
  public void mutateRowWithHardTimeoutErrorHandling()
      throws ExecutionException, InterruptedException, TimeoutException {
    BigtableDataClient mockClient = Mockito.mock(BigtableDataClient.class);
    MetadataTableDao daoWithMock =
        new MetadataTableDao(mockClient, "test-table", ByteString.copyFromUtf8("test"));
    ApiFuture<Void> mockFuture = mock(ApiFuture.class);
    when(mockClient.mutateRowAsync(any())).thenReturn(mockFuture);

    when(mockFuture.get(40, TimeUnit.SECONDS))
        .thenThrow(TimeoutException.class)
        .thenThrow(InterruptedException.class)
        .thenThrow(ExecutionException.class);
    assertThrows(
        RuntimeException.class,
        () -> daoWithMock.mutateRowWithHardTimeout(RowMutation.create("test", "test").deleteRow()));
    assertThrows(
        RuntimeException.class,
        () -> daoWithMock.mutateRowWithHardTimeout(RowMutation.create("test", "test").deleteRow()));
    assertThrows(
        RuntimeException.class,
        () -> daoWithMock.mutateRowWithHardTimeout(RowMutation.create("test", "test").deleteRow()));
  }