Common System 0.2.0
Common interfaces and patterns for system integration
Loading...
Searching...
No Matches
circuit_breaker.h
Go to the documentation of this file.
1// BSD 3-Clause License
2// Copyright (c) 2025, 🍀☀🌕🌥 🌊
3// See the LICENSE file in the project root for full license information.
4
21#pragma once
22
24#include "circuit_state.h"
25#include "failure_window.h"
26#include "../interfaces/stats.h"
27
28#include <atomic>
29#include <chrono>
30#include <exception>
31#include <memory>
32#include <mutex>
33
35
69public:
70 using clock_type = std::chrono::steady_clock;
71 using time_point = clock_type::time_point;
72
86
91 [[nodiscard]] auto allow_request() -> bool
92 {
93 std::lock_guard<std::mutex> lock(mutex_);
94
95 switch (state_) {
97 return true;
98
100 // Check if timeout has elapsed to attempt recovery
101 if (should_attempt_reset()) {
103 // First request in HALF_OPEN counts toward the limit
105 return true;
106 }
107 return false;
108
110 // Allow limited requests for testing
113 return true;
114 }
115 return false;
116 }
117
118 return false; // Unreachable, but satisfies compiler
119 }
120
125 auto record_success() -> void
126 {
127 std::lock_guard<std::mutex> lock(mutex_);
128
133 }
134 }
135 }
136
142 auto record_failure(const std::exception* e = nullptr) -> void
143 {
144 (void)e; // Reserved for future metrics/logging integration
145
146 std::lock_guard<std::mutex> lock(mutex_);
147
149
151 // Any failure in half-open immediately reopens circuit
153 } else if (state_ == circuit_state::CLOSED) {
154 // Check if failure threshold exceeded
157 }
158 }
159 }
160
165 [[nodiscard]] auto get_state() const -> circuit_state
166 {
167 return state_.load(std::memory_order_acquire);
168 }
169
176 class guard {
177 public:
178 explicit guard(circuit_breaker& breaker)
179 : breaker_(breaker)
180 , committed_(false)
181 {
182 }
183
185 {
186 if (!committed_) {
188 }
189 }
190
191 // Non-copyable, non-movable
192 guard(const guard&) = delete;
193 guard& operator=(const guard&) = delete;
194 guard(guard&&) = delete;
195 guard& operator=(guard&&) = delete;
196
201 auto record_success() -> void
202 {
203 committed_ = true;
205 }
206
207 private:
210 };
211
216 [[nodiscard]] auto make_guard() -> guard { return guard(*this); }
217
218 // IStats interface implementation
219
224 [[nodiscard]] auto get_stats() const -> std::unordered_map<std::string, interfaces::stats_value> override
225 {
226 std::lock_guard<std::mutex> lock(mutex_);
227
228 const auto current_state = state_.load(std::memory_order_acquire);
229 const auto failure_count = failure_window_.get_failure_count();
230
231 return {
232 {"current_state", to_string(current_state)},
233 {"failure_count", static_cast<std::int64_t>(failure_count)},
234 {"consecutive_successes", static_cast<std::int64_t>(consecutive_successes_)},
235 {"half_open_requests", static_cast<std::int64_t>(half_open_requests_)},
236 {"failure_threshold", static_cast<std::int64_t>(config_.failure_threshold)},
237 {"is_open", current_state == circuit_state::OPEN}
238 };
239 }
240
245 [[nodiscard]] auto to_json() const -> std::string override
246 {
247 const auto snapshot = get_snapshot();
248 return snapshot.to_json();
249 }
250
255 [[nodiscard]] auto name() const -> std::string_view override
256 {
257 return "circuit_breaker";
258 }
259
260private:
265 [[nodiscard]] auto should_attempt_reset() const -> bool
266 {
267 const auto now = clock_type::now();
268 const auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(
269 now - last_state_change_);
270 return elapsed >= config_.timeout;
271 }
272
277 auto transition_to_closed() -> void
278 {
279 state_.store(circuit_state::CLOSED, std::memory_order_release);
283 last_state_change_ = clock_type::now();
284 }
285
290 auto transition_to_open() -> void
291 {
292 state_.store(circuit_state::OPEN, std::memory_order_release);
295 last_state_change_ = clock_type::now();
296 }
297
303 {
304 state_.store(circuit_state::HALF_OPEN, std::memory_order_release);
307 last_state_change_ = clock_type::now();
308 }
309
311 std::atomic<circuit_state> state_;
316 mutable std::mutex mutex_;
317};
318
319} // namespace kcenon::common::resilience
Configuration structure for circuit breaker behavior.
Circuit breaker state machine states.
Interface for components that expose statistics.
virtual auto get_snapshot() const -> stats_snapshot
Get a complete statistics snapshot with metadata.
RAII guard for automatic success/failure recording.
auto record_success() -> void
Explicitly mark operation as successful. Prevents automatic failure recording on destruction.
Thread-safe circuit breaker for fault tolerance.
auto name() const -> std::string_view override
Get component name for identification.
auto to_json() const -> std::string override
Get statistics as JSON string.
auto record_failure(const std::exception *e=nullptr) -> void
Record a failed operation. May trigger state transition to OPEN or back to OPEN from HALF_OPEN.
circuit_breaker(circuit_breaker_config config={})
Construct circuit breaker with specified configuration.
auto transition_to_half_open() -> void
Transition to HALF_OPEN state. Must be called with mutex locked.
auto transition_to_closed() -> void
Transition to CLOSED state. Must be called with mutex locked.
auto make_guard() -> guard
Create RAII guard for automatic recording.
auto allow_request() -> bool
Check if request should be allowed through the circuit.
auto get_state() const -> circuit_state
Get current circuit state.
auto transition_to_open() -> void
Transition to OPEN state. Must be called with mutex locked.
auto get_stats() const -> std::unordered_map< std::string, interfaces::stats_value > override
Get current statistics as key-value pairs.
auto record_success() -> void
Record a successful operation. May trigger state transition from HALF_OPEN to CLOSED.
auto should_attempt_reset() const -> bool
Check if circuit should attempt reset to HALF_OPEN. Must be called with mutex locked.
Thread-safe sliding window for failure tracking.
auto get_failure_count() const -> std::size_t
Get current failure count within window. Removes expired failures before counting.
auto record_failure() -> void
Record a new failure at current time.
auto reset() -> void
Clear all recorded failures.
Sliding time window for tracking failures.
circuit_state
Represents the current state of a circuit breaker.
@ CLOSED
Normal operation state. Requests are allowed and failures are tracked.
@ HALF_OPEN
Recovery testing state. Limited requests are allowed to test if service has recovered.
@ OPEN
Failure state. Requests are immediately rejected without execution. Transitions to HALF_OPEN after ti...
auto to_string(circuit_state state) -> std::string
Convert circuit state to human-readable string.
Convenience header for statistics interfaces.
Configuration parameters for circuit breaker.
std::chrono::milliseconds timeout
Timeout before transitioning from OPEN to HALF_OPEN. Default: 30 seconds.
std::size_t half_open_max_requests
Maximum number of requests allowed in HALF_OPEN state for testing. Default: 3 requests.
std::size_t failure_threshold
Number of failures required to trip the circuit (CLOSED -> OPEN). Default: 5 failures.
std::size_t success_threshold
Number of successful requests required to close the circuit (HALF_OPEN -> CLOSED)....