@Override
  public void put(PageId pageId,
      ByteBuffer page,
      boolean isTemporary) throws IOException {
    Callable<Void> callable = () -> {
      mPageStore.put(pageId, page, isTemporary);
      return null;
    };
    try {
      mTimeLimter.callWithTimeout(callable, mTimeoutMs, TimeUnit.MILLISECONDS);
    } catch (InterruptedException e) {
      // Task got cancelled by others, interrupt the current thread
      // and then throw a runtime ex to make the higher level stop.
      Thread.currentThread().interrupt();
      throw new RuntimeException(e);
    } catch (TimeoutException e) {
      Metrics.STORE_PUT_TIMEOUT.inc();
      throw new IOException(e);
    } catch (RejectedExecutionException e) {
      Metrics.STORE_THREADS_REJECTED.inc();
      throw new IOException(e);
    } catch (ExecutionException e) {
      Throwables.propagateIfPossible(e.getCause(), ResourceExhaustedException.class,
          IOException.class);
      throw new IOException(e);
    } catch (Throwable t) {
      Throwables.propagateIfPossible(t, IOException.class);
      throw new IOException(t);
    }
  }
@Test
  public void putTimeout() throws Exception {
    mPageStore.setPutHanging(true);
    try {
      mTimeBoundPageStore.put(PAGE_ID, PAGE);
      fail();
    } catch (IOException e) {
      assertTrue(e.getCause() instanceof TimeoutException);
    }
  }