Repository: resilience4j/resilience4j (Java)

Issue:
NullPointerException when using CircuitBreakerConfig.Sliding Window Synchronization Strategy. LOCK_FREE
Exception occurs when using 'CircuitBreakerConfig.Sliding Window Synchronization Strategy. LOCK_FREE' and configuring 'sliding window size' as 1 while using 'TIME-BASED'
```java
java.lang.NullPointerException: Cannot read field "next" because "head" is null
	at io.github.resilience4j.core.metrics.LockFreeSlidingTimeWindowMetrics.updateWindow(LockFreeSlidingTimeWindowMetrics.java:175)
	at io.github.resilience4j.core.metrics.LockFreeSlidingTimeWindowMetrics.advanceTimeSlice(LockFreeSlidingTimeWindowMetrics.java:168)
	at io.github.resilience4j.core.metrics.LockFreeSlidingTimeWindowMetrics.getSnapshot(LockFreeSlidingTimeWindowMetrics.java:120)
	at io.github.resilience4j.circuitbreaker.internal.CircuitBreakerMetrics.getFailureRate(CircuitBreakerMetrics.java:205)
```


Discussion: <!-- Answer -->
This NullPointerException happens because the lock-free time-based sliding window implementation in Resilience4j doesn't handle the edge case where the window size is set to 1. When the window size is 1, the linked list used internally only has a single node, and the code tries to access the next node without checking for null, which leads to the exception you see ([source](https://github.com/resilience4j/resilience4j/blob/4ca330e7c5cabae9411d977fb608ea80bbeab7dd/resilience4j-core/src/main/java/io/github/resilience4j/core/metrics/LockFreeSlidingTimeWindowMetrics.java)).

There's no explicit documentation or code preventing this configuration, but it's not handled safely in the current implementation. To avoid this issue, use a sliding window size greater than 1 with the LOCK_FREE strategy, or switch to the SYNCHRONIZED strategy if you need a window size of 1 ([source](https://github.com/resilience4j/resilience4j/blob/4ca330e7c5cabae9411d977fb608ea80bbeab7dd/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java)).


<!-- Dosu Comment Footer -->
*To reply, just mention [@dosu](https://go.dosu.dev/dosubot).*

---
<sup>How did I do? [Good](https://app.dosu.dev/response-feedback/062b4954-ecbd-4f34-886f-3de53160bfdb?feedback_type=great_response) | [Irrelevant](https://app.dosu.dev/response-feedback/062b4954-ecbd-4f34-886f-3de53160bfdb?feedback_type=irrelevant_answer) | [Incorrect](https://app.dosu.dev/response-feedback/062b4954-ecbd-4f34-886f-3de53160bfdb?feedback_type=incorrect_sources) | [Verbose](https://app.dosu.dev/response-feedback/062b4954-ecbd-4f34-886f-3de53160bfdb?feedback_type=too_verbose) | [Hallucination](https://app.dosu.dev/response-feedback/062b4954-ecbd-4f34-886f-3de53160bfdb?feedback_type=hallucination) | [Report 🐛](https://app.dosu.dev/response-feedback/062b4954-ecbd-4f34-886f-3de53160bfdb?feedback_type=bug_report) | [Other](https://app.dosu.dev/response-feedback/062b4954-ecbd-4f34-886f-3de53160bfdb?feedback_type=other)</sup>&nbsp;&nbsp;[![Join Discord](https://img.shields.io/badge/join-5865F2?logo=discord&logoColor=white&label=)](https://go.dosu.dev/discord-bot)&nbsp;[![Share on X](https://img.shields.io/badge/X-share-black)](https://twitter.com/intent/tweet?text=%40dosu_ai%20helped%20me%20solve%20this%20issue!&url=https%3A//github.com/resilience4j/resilience4j/issues/2334)
@dosu
I’d like to contribute this issue
Is it okay if I take this issue and prepare an implementation?
Yes sure. Any contributions are welcome 




Fix (patch):
diff --git a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java
index 8709fa2d8..643d2623a 100644
--- a/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java
+++ b/resilience4j-circuitbreaker/src/main/java/io/github/resilience4j/circuitbreaker/CircuitBreakerConfig.java
@@ -656,6 +656,9 @@ public Builder permittedNumberOfCallsInHalfOpenState(
          * @return the CircuitBreakerConfig.Builder
          * @throws IllegalArgumentException if {@code slidingWindowSize < 1 || minimumNumberOfCalls
          *                                  < 1}
+         *                                  or if {@code slidingWindowType == TIME_BASED
+         *                                  && slidingWindowSynchronizationStrategy == LOCK_FREE
+         *                                  && slidingWindowSize < 2}
          */
         public Builder slidingWindow(
                 int slidingWindowSize,
@@ -674,6 +677,11 @@ public Builder slidingWindow(
             } else {
                 this.minimumNumberOfCalls = minimumNumberOfCalls;
             }
+
+            if(slidingWindowType == SlidingWindowType.TIME_BASED && slidingWindowSynchronizationStrategy == SlidingWindowSynchronizationStrategy.LOCK_FREE && slidingWindowSize < 2) {
+                throw new IllegalArgumentException("For TIME_BASED with LOCK_FREE strategy, slidingWindowSize must be at least 2");
+            }
+
             this.slidingWindowSize = slidingWindowSize;
             this.slidingWindowType = slidingWindowType;
             this.slidingWindowSynchronizationStrategy = slidingWindowSynchronizationStrategy;