Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions commander/src/main/java/com/iluwatar/commander/Retry.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;

Expand All @@ -53,6 +56,8 @@ public interface HandleErrorIssue<T> {
}

private static final SecureRandom RANDOM = new SecureRandom();
private static final ScheduledExecutorService scheduler =
Executors.newSingleThreadScheduledExecutor();

Comment on lines +59 to 61
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential concurrency risk: a static singleton scheduler is used to drive retries for all Retry instances. This can cause race conditions on shared state (errors/list) if multiple invocations run in parallel. Consider per-instance scheduling or ensure proper lifecycle shutdown of the scheduler, and make shared state thread-safe.

private final Operation op;
private final HandleErrorIssue<T> handleError;
Expand Down Expand Up @@ -98,9 +103,23 @@ public void perform(List<Exception> list, T obj) {
long testDelay =
(long) Math.pow(2, this.attempts.intValue()) * 1000 + RANDOM.nextInt(1000);
long delay = Math.min(testDelay, this.maxDelay);
Thread.sleep(delay);
} catch (InterruptedException f) {
// ignore
scheduler.schedule(() -> perform(list, obj), delay, TimeUnit.MILLISECONDS);
return;
} catch (Exception j) {
this.errors.add(j);

if (this.attempts.incrementAndGet() >= this.maxAttempts || !this.test.test(j)) {
this.handleError.handleIssue(obj, j);
return;
}

long testDelay =
(long) Math.pow(2, this.attempts.intValue()) * 1000 + RANDOM.nextInt(1000);

long delay = Math.min(testDelay, this.maxDelay);

scheduler.schedule(() -> perform(list, obj), delay, TimeUnit.MILLISECONDS);
return;
}
Comment on lines 103 to 123
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This backoff now uses a scheduled task to retry. Watch for concurrency: the same 'list' and 'errors' structures are captured and reused by the scheduled task, which may execute in parallel with the caller thread. If 'errors' is not thread-safe, this may cause concurrent modification exceptions or data races. Recommend making 'errors' a thread-safe list (e.g., CopyOnWriteArrayList) or guarding modifications with synchronization. Also consider whether a per-attempt scheduling model is intended to share a single scheduler across all retries; if not, scope the scheduler to the Retry instance.

}
} while (true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
package com.iluwatar.logaggregation;

import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -44,7 +44,8 @@ public class LogAggregator {
private final CentralLogStore centralLogStore;
private final ConcurrentLinkedQueue<LogEntry> buffer = new ConcurrentLinkedQueue<>();
private final LogLevel minLogLevel;
private final ExecutorService executorService = Executors.newSingleThreadExecutor();
private final ScheduledExecutorService executorService =
Executors.newSingleThreadScheduledExecutor();
private final AtomicInteger logCount = new AtomicInteger(0);

/**
Expand Down Expand Up @@ -104,16 +105,6 @@ private void flushBuffer() {
}

private void startBufferFlusher() {
executorService.execute(
() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(5000); // Flush every 5 seconds.
flushBuffer();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
executorService.scheduleAtFixedRate(this::flushBuffer, 5, 5, TimeUnit.SECONDS);
}
}
12 changes: 12 additions & 0 deletions microservices-self-registration/eurekaserver/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.diffplug.spotless</groupId>
<artifactId>spotless-maven-plugin</artifactId>
<version>2.44.4</version>
<configuration>
<java>
<googleJavaFormat>
<version>1.17.0</version>
</googleJavaFormat>
</java>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
@EnableEurekaServer
public class EurekaserverApplication {

public static void main(String[] args) {
SpringApplication.run(EurekaserverApplication.class, args);
}

public static void main(String[] args) {
SpringApplication.run(EurekaserverApplication.class, args);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
@SpringBootTest
class EurekaserverApplicationTests {

@Test
void contextLoads() {
// This is a basic integration test that checks if the Spring Application Context loads successfully.
@Test
void contextLoads() {
// This is a basic integration test that checks if the Spring Application Context loads
// successfully.
// If the context loads without any exceptions, the test is considered passing.
// It is often left empty as the act of loading the context is the primary verification.
// You can add specific assertions here if you want to verify the presence or state of certain beans.
}

// You can add specific assertions here if you want to verify the presence or state of certain
// beans.
}
}
Loading
Loading