@Override
    public boolean shouldSample() {
        // This load might race with the store below, causing multiple threads to get a sample
        // since the new timestamp has not been written yet, but it is extremely unlikely and
        // the consequences are not severe since this is a probabilistic sampler that does not
        // provide hard lower or upper bounds.
        long lastSampledAt = lastSampledAtNanoTime.get(); // TODO getPlain? No transitive visibility requirements
        long now = nanoClock.nanoTimeNow();
        double secsSinceLastSample = (now - lastSampledAt) / 1_000_000_000.0;
        // As the time elapsed since last sample increases, so does the probability of a new sample
        // being selected.
        double sampleProb = Math.min(secsSinceLastSample * desiredSamplesPerSec, 1.0);
        if (randomSupplier.get().nextDouble() < sampleProb) {
            lastSampledAtNanoTime.set(now); // TODO setPlain? No transitive visibility requirements
            return true;
        } else {
            return false;
        }
    }
@Test
    void samples_are_rate_limited_per_second() {
        var clock = MockUtils.mockedClockReturning(ms2ns(10_000), ms2ns(10_500), ms2ns(10_500), ms2ns(10_501));
        var rng = MockUtils.mockedRandomReturning(0.1, 0.51, 0.49, 0.01);
        var sampler = new ProbabilisticSampleRate(clock, () -> rng, 1.0);
        // 1st invocation, 10 seconds (technically "infinity") since last sample. P = 1.0, sampled
        assertTrue(sampler.shouldSample());
        // 2nd invocation, 0.5 seconds since last sample. rng = 0.51 >= P = 0.5, not sampled
        assertFalse(sampler.shouldSample());
        // 3rd invocation, 0.5 seconds since last sample. rng = 0.49 < P = 0.5, sampled
        assertTrue(sampler.shouldSample());
        // 4th invocation, 0.001 seconds since last sample. rng = 0.01 >= P = 0.001, not sampled
        assertFalse(sampler.shouldSample());
    }