refactoring(ping-service)

TODO-Roadmap.md
This commit is contained in:
stefan
2025-08-14 12:30:31 +02:00
parent 9811eb6130
commit d937e82d2b
11 changed files with 1242 additions and 73 deletions
@@ -1,13 +1,45 @@
package at.mocode.temp.pingservice
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
class PingController {
class PingController(
private val pingServiceCircuitBreaker: PingServiceCircuitBreaker
) {
/**
* Standard ping endpoint - maintains backward compatibility
*/
@GetMapping("/ping")
fun ping(): Map<String, String> {
return mapOf("status" to "pong")
}
/**
* Enhanced ping endpoint with circuit breaker protection
*
* @param simulate - whether to simulate failures for testing circuit breaker
*/
@GetMapping("/ping/enhanced")
fun enhancedPing(@RequestParam(defaultValue = "false") simulate: Boolean): Map<String, Any> {
return pingServiceCircuitBreaker.ping(simulate)
}
/**
* Health check endpoint with circuit breaker protection
*/
@GetMapping("/ping/health")
fun health(): Map<String, Any> {
return pingServiceCircuitBreaker.healthCheck()
}
/**
* Endpoint to test circuit breaker behavior by forcing failures
*/
@GetMapping("/ping/test-failure")
fun testFailure(): Map<String, Any> {
return pingServiceCircuitBreaker.ping(simulateFailure = true)
}
}
@@ -0,0 +1,104 @@
package at.mocode.temp.pingservice
import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker
import org.slf4j.LoggerFactory
import org.springframework.stereotype.Service
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import kotlin.random.Random
/**
* Service demonstrating a Circuit Breaker pattern with Resilience
*
* This service simulates potential failures and uses circuit breaker
* to handle service degradation gracefully with fallback responses.
*/
@Service
class PingServiceCircuitBreaker {
private val logger = LoggerFactory.getLogger(PingServiceCircuitBreaker::class.java)
companion object {
const val PING_CIRCUIT_BREAKER = "pingCircuitBreaker"
private val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
}
/**
* Primary ping method with circuit breaker protection
*
* @param simulateFailure - if true, randomly throws exceptions to test circuit breaker
* @return Map containing ping response with timestamp and status
*/
@CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackPing")
fun ping(simulateFailure: Boolean = false): Map<String, Any> {
logger.info("Executing ping service call...")
if (simulateFailure && Random.nextDouble() < 0.6) {
logger.warn("Simulating service failure for circuit breaker testing")
throw RuntimeException("Simulated service failure")
}
val currentTime = LocalDateTime.now().format(formatter)
logger.info("Ping service call successful")
return mapOf(
"status" to "pong",
"timestamp" to currentTime,
"service" to "ping-service",
"circuitBreaker" to "CLOSED"
)
}
/**
* Fallback method called when circuit breaker is OPEN
*
* @param simulateFailure - original parameter (ignored in fallback)
* @param exception - the exception that triggered the fallback
* @return Map containing fallback response
*/
fun fallbackPing(simulateFailure: Boolean = false, exception: Exception): Map<String, Any> {
logger.warn("Circuit breaker fallback triggered due to: {}", exception.message)
val currentTime = LocalDateTime.now().format(formatter)
return mapOf(
"status" to "fallback",
"message" to "Service temporarily unavailable",
"timestamp" to currentTime,
"service" to "ping-service-fallback",
"circuitBreaker" to "OPEN",
"error" to (exception.message ?: "Unknown error")
)
}
/**
* Health check method with circuit breaker protection
*/
@CircuitBreaker(name = PING_CIRCUIT_BREAKER, fallbackMethod = "fallbackHealth")
fun healthCheck(): Map<String, Any> {
logger.info("Executing health check...")
// Health check is now deterministic for reliable integration testing
// Random failures were causing intermittent test failures
return mapOf(
"status" to "UP",
"timestamp" to LocalDateTime.now().format(formatter),
"circuitBreaker" to "CLOSED"
)
}
/**
* Fallback for health check
*/
fun fallbackHealth(exception: Exception): Map<String, Any> {
logger.warn("Health check fallback triggered: {}", exception.message)
return mapOf(
"status" to "DOWN",
"message" to "Health check temporarily unavailable",
"timestamp" to LocalDateTime.now().format(formatter),
"circuitBreaker" to "OPEN"
)
}
}
@@ -17,7 +17,45 @@ management:
endpoints:
web:
exposure:
include: health,info
include: health,info,circuitbreakers
endpoint:
health:
show-details: always
# Resilience4j Circuit Breaker Configuration
resilience4j:
circuitbreaker:
configs:
default:
# Circuit breaker opens when the failure rate exceeds 50%
failure-rate-threshold: 50
# Minimum number of calls to calculate the failure rate
minimum-number-of-calls: 5
# Time to wait before transitioning from OPEN to HALF_OPEN
wait-duration-in-open-state: 10s
# Number of calls in HALF_OPEN state before deciding to close/open
permitted-number-of-calls-in-half-open-state: 3
# Sliding window size for calculating failure rate
sliding-window-size: 10
# Type of sliding window (COUNT_BASED or TIME_BASED)
sliding-window-type: COUNT_BASED
# Record exceptions that should be considered as failures
record-exceptions:
- java.lang.Exception
# Ignore certain exceptions (don't count as failures)
ignore-exceptions:
- java.lang.IllegalArgumentException
instances:
pingCircuitBreaker:
# Use default configuration
base-config: default
# Override specific settings for this instance if needed
failure-rate-threshold: 60
minimum-number-of-calls: 4
wait-duration-in-open-state: 5s
# Optional: Metrics configuration
metrics:
enabled: true
legacy:
enabled: true