2525/** Remote sampler that gets sampling configuration from AWS X-Ray. */
2626public final class AwsXrayRemoteSampler implements Sampler , Closeable {
2727
28+ private static final Random RANDOM = new Random ();
29+
2830 private static final Logger logger = Logger .getLogger (AwsXrayRemoteSampler .class .getName ());
2931
3032 private static final String WORKER_THREAD_NAME =
@@ -37,8 +39,10 @@ public final class AwsXrayRemoteSampler implements Sampler, Closeable {
3739 private final Sampler initialSampler ;
3840 private final XraySamplerClient client ;
3941 private final ScheduledExecutorService executor ;
40- private final ScheduledFuture <?> pollFuture ;
42+ private final long pollingIntervalNanos ;
43+ private final int jitterNanos ;
4144
45+ @ Nullable private volatile ScheduledFuture <?> pollFuture ;
4246 @ Nullable private volatile GetSamplingRulesResponse previousRulesResponse ;
4347 private volatile Sampler sampler ;
4448
@@ -72,9 +76,12 @@ public static AwsXrayRemoteSamplerBuilder newBuilder(Resource resource) {
7276
7377 sampler = initialSampler ;
7478
75- pollFuture =
76- executor .scheduleAtFixedRate (
77- this ::getAndUpdateSampler , 0 , pollingIntervalNanos , TimeUnit .NANOSECONDS );
79+ this .pollingIntervalNanos = pollingIntervalNanos ;
80+ // Add ~1% of jitter. Truncating to int is safe for any practical polling interval.
81+ jitterNanos = (int ) (pollingIntervalNanos / 100 );
82+
83+ // Execute first update right away on the executor thread.
84+ executor .execute (this ::getAndUpdateSampler );
7885 }
7986
8087 @ Override
@@ -106,11 +113,20 @@ private void getAndUpdateSampler() {
106113 } catch (Throwable t ) {
107114 logger .log (Level .FINE , "Failed to update sampler" , t );
108115 }
116+ scheduleSamplerUpdate ();
117+ }
118+
119+ private void scheduleSamplerUpdate () {
120+ long delay = pollingIntervalNanos + RANDOM .nextInt (jitterNanos );
121+ pollFuture = executor .schedule (this ::getAndUpdateSampler , delay , TimeUnit .NANOSECONDS );
109122 }
110123
111124 @ Override
112125 public void close () {
113- pollFuture .cancel (true );
126+ ScheduledFuture <?> pollFuture = this .pollFuture ;
127+ if (pollFuture != null ) {
128+ pollFuture .cancel (true );
129+ }
114130 executor .shutdownNow ();
115131 // No flushing behavior so no need to wait for the shutdown.
116132 }
0 commit comments