@VisibleForTesting
  long getJmxCacheTTL() {
    return jmxCacheTTL;
  }
@Test
  public void testMetricCacheUpdateRace() throws Exception {
    // Create test source with a single metric counter of value 1.
    TestMetricsSource source = new TestMetricsSource();
    MetricsSourceBuilder sourceBuilder =
        MetricsAnnotations.newSourceBuilder(source);

    final long JMX_CACHE_TTL = 250; // ms
    List<MetricsTag> injectedTags = new ArrayList<>();
    MetricsSourceAdapter sourceAdapter =
        new MetricsSourceAdapter("test", "test",
            "test JMX cache update race condition", sourceBuilder.build(),
            injectedTags, null, null, JMX_CACHE_TTL, false);

    ScheduledExecutorService updaterExecutor =
        Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder().build());
    ScheduledExecutorService readerExecutor =
        Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder().build());

    final AtomicBoolean hasError = new AtomicBoolean(false);

    // Wake up every 1 JMX cache TTL to set lastRecs before updateJmxCache() is
    // called.
    SourceUpdater srcUpdater = new SourceUpdater(sourceAdapter, hasError);
    ScheduledFuture<?> updaterFuture =
        updaterExecutor.scheduleAtFixedRate(srcUpdater,
            sourceAdapter.getJmxCacheTTL(), sourceAdapter.getJmxCacheTTL(),
            TimeUnit.MILLISECONDS);
    srcUpdater.setFuture(updaterFuture);

    // Wake up every 2 JMX cache TTL so updateJmxCache() will try to update
    // JMX cache.
    SourceReader srcReader = new SourceReader(source, sourceAdapter, hasError);
    ScheduledFuture<?> readerFuture =
        readerExecutor.scheduleAtFixedRate(srcReader,
            0, // set JMX info cache at the beginning
            2 * sourceAdapter.getJmxCacheTTL(), TimeUnit.MILLISECONDS);
    srcReader.setFuture(readerFuture);

    // Let the threads do their work.
    Thread.sleep(RACE_TEST_RUNTIME);

    assertFalse("Hit error", hasError.get());

    // cleanup
    updaterExecutor.shutdownNow();
    readerExecutor.shutdownNow();
    updaterExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
    readerExecutor.awaitTermination(1000, TimeUnit.MILLISECONDS);
  }