Network System 0.1.1
High-performance modular networking library for scalable client-server applications
Loading...
Searching...
No Matches
kcenon::network::utils::resilient_client Class Reference

Wrapper around messaging_client that adds automatic reconnection with exponential backoff and circuit breaker pattern. More...

#include <resilient_client.h>

Collaboration diagram for kcenon::network::utils::resilient_client:
Collaboration graph

Public Member Functions

 resilient_client (const std::string &client_id, const std::string &host, unsigned short port, size_t max_retries=3, std::chrono::milliseconds initial_backoff=std::chrono::seconds(1))
 Constructs a resilient client with reconnection support.
 
 ~resilient_client () noexcept
 Destructor - disconnects client if still connected.
 
auto connect () -> VoidResult
 Connects to the server with retry logic.
 
auto disconnect () -> VoidResult
 Disconnects from the server.
 
auto send_with_retry (std::vector< uint8_t > &&data) -> VoidResult
 Sends data with automatic reconnection on failure.
 
auto is_connected () const noexcept -> bool
 Checks if currently connected to server.
 
auto set_reconnect_callback (std::function< void(size_t attempt)> callback) -> void
 Sets callback for reconnection events.
 
auto set_disconnect_callback (std::function< void()> callback) -> void
 Sets callback for connection loss events.
 
auto get_client () const -> std::shared_ptr< core::messaging_client >
 Gets the underlying messaging client.
 

Private Member Functions

auto reconnect () -> VoidResult
 Attempts to reconnect with exponential backoff.
 
auto calculate_backoff (size_t attempt) const -> std::chrono::milliseconds
 Calculates backoff duration for given attempt.
 

Private Attributes

std::shared_ptr< core::messaging_clientclient_
 
std::string host_
 
unsigned short port_
 
size_t max_retries_
 
std::chrono::milliseconds initial_backoff_
 
std::atomic< bool > is_connected_ {false}
 
std::function< void(size_t)> reconnect_callback_
 
std::function< void()> disconnect_callback_
 

Detailed Description

Wrapper around messaging_client that adds automatic reconnection with exponential backoff and circuit breaker pattern.

Thread Safety

  • All public methods are thread-safe
  • Reconnection logic is protected by internal state management
  • Callbacks are invoked on reconnection thread

Key Features

  • Automatic reconnection on connection loss
  • Exponential backoff to prevent connection storms
  • Configurable retry behavior (max attempts, backoff)
  • Circuit breaker pattern to prevent cascade failures
  • Callback notifications for reconnection events
  • Graceful degradation on persistent failures

Circuit Breaker Integration

The circuit breaker prevents excessive retry attempts when the backend is unavailable. When the circuit opens, send_with_retry() will fail immediately without attempting network calls.

Usage Example

auto client = std::make_shared<resilient_client>(
"client_id", "localhost", 8080,
3, // max retries
std::chrono::seconds(1), // initial backoff
common::resilience::circuit_breaker_config{
.failure_threshold = 5,
.timeout = std::chrono::seconds(30)
}
);
client->set_reconnect_callback([](size_t attempt) {
std::cout << "Reconnecting (attempt " << attempt << ")\n";
});
auto result = client->connect();
if (!result) {
std::cerr << "Failed to connect\n";
}
// Send with automatic retry and circuit breaker protection
std::vector<uint8_t> data = {1, 2, 3};
auto send_result = client->send_with_retry(std::move(data));
// Check circuit state
if (client->circuit_state() == common::resilience::circuit_state::OPEN) {
std::cerr << "Circuit is open, backend unavailable\n";
}

Definition at line 80 of file resilient_client.h.

Constructor & Destructor Documentation

◆ resilient_client()

kcenon::network::utils::resilient_client::resilient_client ( const std::string & client_id,
const std::string & host,
unsigned short port,
size_t max_retries = 3,
std::chrono::milliseconds initial_backoff = std::chrono::seconds(1) )

Constructs a resilient client with reconnection support.

Parameters
client_idClient identifier
hostServer hostname or IP address
portServer port number
max_retriesMaximum number of reconnection attempts (default: 3)
initial_backoffInitial backoff duration (default: 1 second)
cb_configCircuit breaker configuration (default values if not specified)

Definition at line 14 of file resilient_client.cpp.

23 : host_(host)
24 , port_(port)
25 , max_retries_(max_retries)
26 , initial_backoff_(initial_backoff)
27#ifdef WITH_COMMON_SYSTEM
28 , circuit_breaker_(std::make_unique<common::resilience::circuit_breaker>(cb_config))
29#endif
30 {
31 client_ = std::make_shared<core::messaging_client>(client_id);
32#ifdef WITH_COMMON_SYSTEM
33 NETWORK_LOG_INFO("[resilient_client] Created with max_retries=" +
34 std::to_string(max_retries) + ", initial_backoff=" +
35 std::to_string(initial_backoff.count()) + "ms" +
36 ", circuit_breaker failure_threshold=" +
37 std::to_string(cb_config.failure_threshold));
38#else
39 NETWORK_LOG_INFO("[resilient_client] Created with max_retries=" +
40 std::to_string(max_retries) + ", initial_backoff=" +
41 std::to_string(initial_backoff.count()) + "ms");
42#endif
43 }
std::shared_ptr< core::messaging_client > client_
#define NETWORK_LOG_INFO(msg)

References client_, and NETWORK_LOG_INFO.

◆ ~resilient_client()

kcenon::network::utils::resilient_client::~resilient_client ( )
noexcept

Destructor - disconnects client if still connected.

Definition at line 45 of file resilient_client.cpp.

46 {
47 try
48 {
49 if (is_connected_.load())
50 {
51 (void)disconnect();
52 }
53 }
54 catch (...)
55 {
56 // Destructor must not throw
57 }
58 }
auto disconnect() -> VoidResult
Disconnects from the server.

References disconnect(), and is_connected_.

Here is the call graph for this function:

Member Function Documentation

◆ calculate_backoff()

auto kcenon::network::utils::resilient_client::calculate_backoff ( size_t attempt) const -> std::chrono::milliseconds
private

Calculates backoff duration for given attempt.

Parameters
attemptCurrent attempt number (1-based)
Returns
Backoff duration in milliseconds

Definition at line 310 of file resilient_client.cpp.

312 {
313 // Exponential backoff: initial_backoff * 2^(attempt-1)
314 // Capped at 30 seconds to prevent excessive delays
315 auto backoff = initial_backoff_ * (1 << (attempt - 1));
316 auto max_backoff = std::chrono::duration_cast<std::chrono::milliseconds>(
317 std::chrono::seconds(30));
318
319 return std::min(backoff, max_backoff);
320 }

◆ connect()

auto kcenon::network::utils::resilient_client::connect ( ) -> VoidResult

Connects to the server with retry logic.

Returns
Result<void> - Success if connected, error otherwise

Attempts to connect with exponential backoff between retries. Invokes reconnect_callback_ on each retry attempt.

Definition at line 60 of file resilient_client.cpp.

61 {
62 if (is_connected_.load())
63 {
64 return ok();
65 }
66
67 for (size_t attempt = 1; attempt <= max_retries_; ++attempt)
68 {
69 NETWORK_LOG_INFO("[resilient_client] Connection attempt " +
70 std::to_string(attempt) + "/" + std::to_string(max_retries_));
71
72 // Invoke reconnect callback if set
74 {
75 reconnect_callback_(attempt);
76 }
77
78 // Attempt to connect
79 auto result = client_->start_client(host_, port_);
80 if (!result.is_err())
81 {
82 is_connected_.store(true);
83 NETWORK_LOG_INFO("[resilient_client] Connected successfully on attempt " +
84 std::to_string(attempt));
85 return ok();
86 }
87
88 NETWORK_LOG_WARN("[resilient_client] Connection attempt " +
89 std::to_string(attempt) + " failed: " + result.error().message);
90
91 // Don't sleep after last failed attempt
92 if (attempt < max_retries_)
93 {
94 auto backoff = calculate_backoff(attempt);
95 NETWORK_LOG_INFO("[resilient_client] Backing off for " +
96 std::to_string(backoff.count()) + "ms");
97 std::this_thread::sleep_for(backoff);
98 }
99 }
100
101 return error_void(
103 "Failed to connect after " + std::to_string(max_retries_) + " attempts",
104 "resilient_client::connect",
105 "Host: " + host_ + ", Port: " + std::to_string(port_)
106 );
107 }
auto calculate_backoff(size_t attempt) const -> std::chrono::milliseconds
Calculates backoff duration for given attempt.
std::function< void(size_t)> reconnect_callback_
#define NETWORK_LOG_WARN(msg)
VoidResult error_void(int code, const std::string &message, const std::string &source="network_system", const std::string &details="")
VoidResult ok()

References kcenon::network::error_codes::network_system::connection_failed, kcenon::network::error_void(), NETWORK_LOG_INFO, NETWORK_LOG_WARN, and kcenon::network::ok().

Here is the call graph for this function:

◆ disconnect()

auto kcenon::network::utils::resilient_client::disconnect ( ) -> VoidResult

Disconnects from the server.

Returns
Result<void> - Success if disconnected, error otherwise

Definition at line 109 of file resilient_client.cpp.

110 {
111 if (!is_connected_.load())
112 {
113 return ok();
114 }
115
116 auto result = client_->stop_client();
117 if (!result.is_err())
118 {
119 is_connected_.store(false);
120
121 // Invoke disconnect callback if set
123 {
125 }
126
127 NETWORK_LOG_INFO("[resilient_client] Disconnected successfully");
128 }
129 else
130 {
131 NETWORK_LOG_ERROR("[resilient_client] Failed to disconnect: " +
132 result.error().message);
133 }
134
135 return result;
136 }
#define NETWORK_LOG_ERROR(msg)

References NETWORK_LOG_ERROR, NETWORK_LOG_INFO, and kcenon::network::ok().

Referenced by ~resilient_client().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ get_client()

auto kcenon::network::utils::resilient_client::get_client ( ) const -> std::shared_ptr<core::messaging_client>
nodiscard

Gets the underlying messaging client.

Returns
Shared pointer to the messaging_client

Definition at line 259 of file resilient_client.cpp.

261 {
262 return client_;
263 }

References client_.

◆ is_connected()

auto kcenon::network::utils::resilient_client::is_connected ( ) const -> bool
nodiscardnoexcept

Checks if currently connected to server.

Returns
true if connected, false otherwise

Definition at line 242 of file resilient_client.cpp.

243 {
244 return is_connected_.load() && client_->is_connected();
245 }

References client_, and is_connected_.

◆ reconnect()

auto kcenon::network::utils::resilient_client::reconnect ( ) -> VoidResult
private

Attempts to reconnect with exponential backoff.

Returns
Result<void> - Success if reconnected, error otherwise

Definition at line 265 of file resilient_client.cpp.

266 {
267 NETWORK_LOG_INFO("[resilient_client] Attempting reconnection");
268
269 // Ensure client is disconnected first
270 if (client_->is_connected())
271 {
272 (void)client_->stop_client();
273 }
274
275 // Try to reconnect with retry logic
276 for (size_t attempt = 1; attempt <= max_retries_; ++attempt)
277 {
279 {
280 reconnect_callback_(attempt);
281 }
282
283 auto result = client_->start_client(host_, port_);
284 if (!result.is_err())
285 {
286 is_connected_.store(true);
287 NETWORK_LOG_INFO("[resilient_client] Reconnected successfully on attempt " +
288 std::to_string(attempt));
289 return ok();
290 }
291
292 NETWORK_LOG_WARN("[resilient_client] Reconnection attempt " +
293 std::to_string(attempt) + " failed: " + result.error().message);
294
295 if (attempt < max_retries_)
296 {
297 auto backoff = calculate_backoff(attempt);
298 std::this_thread::sleep_for(backoff);
299 }
300 }
301
302 return error_void(
304 "Failed to reconnect after " + std::to_string(max_retries_) + " attempts",
305 "resilient_client::reconnect",
306 "Host: " + host_ + ", Port: " + std::to_string(port_)
307 );
308 }

References kcenon::network::error_codes::network_system::connection_failed, kcenon::network::error_void(), NETWORK_LOG_INFO, NETWORK_LOG_WARN, and kcenon::network::ok().

Here is the call graph for this function:

◆ send_with_retry()

auto kcenon::network::utils::resilient_client::send_with_retry ( std::vector< uint8_t > && data) -> VoidResult

Sends data with automatic reconnection on failure.

Parameters
dataData to send (moved for efficiency)
Returns
Result<void> - Success if sent, error after all retries exhausted

If connection is lost, automatically attempts to reconnect before retrying the send operation.

Definition at line 138 of file resilient_client.cpp.

139 {
140#ifdef WITH_COMMON_SYSTEM
141 // Check circuit breaker first
142 if (!circuit_breaker_->allow_request())
143 {
144 NETWORK_LOG_WARN("[resilient_client] Circuit breaker is open, failing fast");
145 return error_void(
147 "Circuit breaker is open",
148 "resilient_client::send_with_retry",
149 "Circuit state: " + common::resilience::to_string(circuit_breaker_->get_state())
150 );
151 }
152#endif
153
154 // Keep a copy of the data for retries
155 auto data_copy = data;
156
157 for (size_t attempt = 1; attempt <= max_retries_; ++attempt)
158 {
159 // Check if connected
160 if (!is_connected_.load() || !client_->is_connected())
161 {
162 NETWORK_LOG_WARN("[resilient_client] Not connected, attempting reconnection");
163
164 // Attempt to reconnect
165 auto reconnect_result = reconnect();
166 if (reconnect_result.is_err())
167 {
168 NETWORK_LOG_ERROR("[resilient_client] Reconnection failed: " +
169 reconnect_result.error().message);
170
171#ifdef WITH_COMMON_SYSTEM
172 // Record failure to circuit breaker
173 circuit_breaker_->record_failure();
174#endif
175
176 // Exponential backoff before next retry
177 if (attempt < max_retries_)
178 {
179 auto backoff = calculate_backoff(attempt);
180 NETWORK_LOG_INFO("[resilient_client] Backing off for " +
181 std::to_string(backoff.count()) + "ms");
182 std::this_thread::sleep_for(backoff);
183 }
184 continue;
185 }
186 }
187
188 // Try to send
189 std::vector<uint8_t> send_data = data_copy; // Make a copy for this attempt
190 auto result = client_->send_packet(std::move(send_data));
191
192 if (!result.is_err())
193 {
194#ifdef WITH_COMMON_SYSTEM
195 // Record success to circuit breaker
196 circuit_breaker_->record_success();
197#endif
198
199 NETWORK_LOG_DEBUG("[resilient_client] Sent " +
200 std::to_string(data_copy.size()) + " bytes successfully");
201 return ok();
202 }
203
204 NETWORK_LOG_WARN("[resilient_client] Send attempt " +
205 std::to_string(attempt) + " failed: " + result.error().message);
206
207#ifdef WITH_COMMON_SYSTEM
208 // Record failure to circuit breaker
209 circuit_breaker_->record_failure();
210#endif
211
212 // Mark as disconnected if send failed
213 is_connected_.store(false);
214
215 // Disconnect the client
216 (void)client_->stop_client();
217
218 // Invoke disconnect callback if set
220 {
222 }
223
224 // Don't sleep after last failed attempt
225 if (attempt < max_retries_)
226 {
227 auto backoff = calculate_backoff(attempt);
228 NETWORK_LOG_INFO("[resilient_client] Backing off for " +
229 std::to_string(backoff.count()) + "ms");
230 std::this_thread::sleep_for(backoff);
231 }
232 }
233
234 return error_void(
236 "Failed to send after " + std::to_string(max_retries_) + " attempts",
237 "resilient_client::send_with_retry",
238 "Data size: " + std::to_string(data_copy.size()) + " bytes"
239 );
240 }
auto reconnect() -> VoidResult
Attempts to reconnect with exponential backoff.
#define NETWORK_LOG_DEBUG(msg)

References kcenon::network::error_codes_ext::network_system::circuit_open, kcenon::network::error_void(), NETWORK_LOG_DEBUG, NETWORK_LOG_ERROR, NETWORK_LOG_INFO, NETWORK_LOG_WARN, kcenon::network::ok(), and kcenon::network::error_codes::network_system::send_failed.

Here is the call graph for this function:

◆ set_disconnect_callback()

auto kcenon::network::utils::resilient_client::set_disconnect_callback ( std::function< void()> callback) -> void

Sets callback for connection loss events.

Parameters
callbackFunction called when connection is lost

Definition at line 253 of file resilient_client.cpp.

255 {
256 disconnect_callback_ = std::move(callback);
257 }

◆ set_reconnect_callback()

auto kcenon::network::utils::resilient_client::set_reconnect_callback ( std::function< void(size_t attempt)> callback) -> void

Sets callback for reconnection events.

Parameters
callbackFunction called on each reconnection attempt

The callback receives the current attempt number (1-based).

Definition at line 247 of file resilient_client.cpp.

249 {
250 reconnect_callback_ = std::move(callback);
251 }

Member Data Documentation

◆ client_

std::shared_ptr<core::messaging_client> kcenon::network::utils::resilient_client::client_
private

Underlying client

Definition at line 180 of file resilient_client.h.

Referenced by get_client(), is_connected(), and resilient_client().

◆ disconnect_callback_

std::function<void()> kcenon::network::utils::resilient_client::disconnect_callback_
private

Disconnection callback

Definition at line 190 of file resilient_client.h.

◆ host_

std::string kcenon::network::utils::resilient_client::host_
private

Server hostname

Definition at line 182 of file resilient_client.h.

◆ initial_backoff_

std::chrono::milliseconds kcenon::network::utils::resilient_client::initial_backoff_
private

Initial backoff duration

Definition at line 185 of file resilient_client.h.

◆ is_connected_

std::atomic<bool> kcenon::network::utils::resilient_client::is_connected_ {false}
private

Connection state

Definition at line 187 of file resilient_client.h.

187{false};

Referenced by is_connected(), and ~resilient_client().

◆ max_retries_

size_t kcenon::network::utils::resilient_client::max_retries_
private

Maximum retry attempts

Definition at line 184 of file resilient_client.h.

◆ port_

unsigned short kcenon::network::utils::resilient_client::port_
private

Server port

Definition at line 183 of file resilient_client.h.

◆ reconnect_callback_

std::function<void(size_t)> kcenon::network::utils::resilient_client::reconnect_callback_
private

Reconnection callback

Definition at line 189 of file resilient_client.h.


The documentation for this class was generated from the following files: