Fix non-determinism of repair fuzz tests by passing randomizer to RetryStrategy

patch by Nivy Kani; reviewed by David Capwell, Jyothsna Konisa for CASSANDRA-21087
This commit is contained in:
Nivy Kani 2026-01-09 11:41:31 -08:00 committed by David Capwell
parent 86e024b416
commit bc9773ea0a
2 changed files with 41 additions and 2 deletions

View File

@ -22,12 +22,16 @@ import java.util.Objects;
import javax.annotation.Nullable;
import accord.utils.RandomSource;
import org.apache.cassandra.config.DurationSpec.LongMillisecondsBound;
import org.apache.cassandra.repair.SharedContext;
import org.apache.cassandra.service.RetryStrategy;
import org.apache.cassandra.service.TimeoutStrategy.LatencySourceFactory;
import org.apache.cassandra.service.WaitStrategy;
import static org.apache.cassandra.service.RetryStrategy.randomizers;
public class RetrySpec
{
public static class MaxAttempt
@ -161,7 +165,9 @@ public class RetrySpec
{
if (!spec.isEnabled())
return WaitStrategy.None.INSTANCE;
return RetryStrategy.parse(spec.baseSleepTime.toMilliseconds() + "ms * 2^attempts <= " + spec.maxSleepTime.toMilliseconds() + "ms,retries=" + (spec.maxAttempts.value - 1), LatencySourceFactory.none());
RandomSource randomSource = RandomSource.wrap(ctx.random().get());
RetryStrategy.WaitRandomizer randomizer = randomizers(randomSource).uniform();
return RetryStrategy.parse((int) (0.5 * spec.baseSleepTime.toMilliseconds()) + "ms * 2^attempts ... " + (int) (1.5 * spec.baseSleepTime.toMilliseconds()) + "ms * 2^attempts <= " + spec.maxSleepTime.toMilliseconds() + "ms,retries=" + (spec.maxAttempts.value - 1), LatencySourceFactory.none(), randomizer);
}
@Override

View File

@ -18,13 +18,21 @@
package org.apache.cassandra.service;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.function.IntFunction;
import java.util.function.Supplier;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import accord.utils.Gen;
import accord.utils.RandomTestRunner;
import org.apache.cassandra.config.DurationSpec;
import org.apache.cassandra.config.RetrySpec;
import org.apache.cassandra.repair.SharedContext;
public class RetryStrategyTest
{
@Test
@ -64,6 +72,31 @@ public class RetryStrategyTest
});
}
@Test
public void seededWaitRandomizer()
{
RetrySpec spec = new RetrySpec(new RetrySpec.MaxAttempt(10),
new DurationSpec.LongMillisecondsBound("200ms"),
new DurationSpec.LongMillisecondsBound("1000ms"));
long wait1 = RetrySpec.toStrategy(sharedContext(100), spec).computeWait(1, TimeUnit.MILLISECONDS);
long wait2 = RetrySpec.toStrategy(sharedContext(100), spec).computeWait(1, TimeUnit.MILLISECONDS);
long wait3 = RetrySpec.toStrategy(sharedContext(200), spec).computeWait(1, TimeUnit.MILLISECONDS);
Assertions.assertThat(wait1).isEqualTo(wait2);
Assertions.assertThat(wait1).isNotEqualTo(wait3);
}
private static SharedContext sharedContext(long seed)
{
return new SharedContext.ForwardingSharedContext(SharedContext.Global.instance)
{
@Override
public Supplier<Random> random()
{
return () -> new Random(seed);
}
};
}
private static class TestLatencySourceFactory implements TimeoutStrategy.LatencySourceFactory
{
@ -73,4 +106,4 @@ public class RetryStrategyTest
return percentile -> 10;
}
}
}
}