6#include <ascii-chat/network/consensus/state.h>
7#include <ascii-chat/common.h>
8#include <ascii-chat/util/time.h>
9#include <ascii-chat/asciichat_errno.h>
17 uint8_t backup_id[16];
39static double compute_metric_score(
const participant_metrics_t *m) {
45 double rtt_score = m->rtt_ns / 1000.0;
46 double bandwidth_penalty = (1000000.0 - m->upload_kbps) / 1000.0;
47 return rtt_score + bandwidth_penalty;
52 if (!my_id || !topology || !out_state) {
53 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid state creation parameters");
57 memset(state, 0,
sizeof(*state));
61 memcpy(state->
my_id, my_id, 16);
85 return SET_ERRNO(ERROR_INVALID_PARAM,
"State is NULL");
89 return SET_ERRNO(ERROR_INVALID_STATE,
"Invalid transition from state %d", state->
current_state);
101 if (!state || !metrics) {
102 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters");
106 return SET_ERRNO(ERROR_INVALID_STATE,
"Invalid state for adding metrics: %d", state->
current_state);
112 participant_metrics_t *new_metrics =
113 SAFE_REALLOC(state->
metrics, new_capacity *
sizeof(participant_metrics_t), participant_metrics_t *);
127 return SET_ERRNO(ERROR_INVALID_PARAM,
"State is NULL");
131 return SET_ERRNO(ERROR_INVALID_STATE,
"Invalid transition from state %d", state->
current_state);
146 return SET_ERRNO(ERROR_INVALID_PARAM,
"State is NULL");
149 if (state->
current_state != CONSENSUS_STATE_ELECTION_START) {
150 return SET_ERRNO(ERROR_INVALID_STATE,
"Invalid transition from state %d", state->
current_state);
154 return SET_ERRNO(ERROR_INVALID_PARAM,
"Need at least 2 metrics for election, have %d", state->
metrics_count);
161 double best_score = compute_metric_score(&state->
metrics[0]);
162 double backup_score = compute_metric_score(&state->
metrics[1]);
165 if (best_score > backup_score) {
167 best_idx = backup_idx;
169 double tmp_score = best_score;
170 best_score = backup_score;
171 backup_score = tmp_score;
176 double score = compute_metric_score(&state->
metrics[i]);
177 if (score < best_score) {
178 backup_idx = best_idx;
179 backup_score = best_score;
182 }
else if (score < backup_score) {
184 backup_score = score;
199 return SET_ERRNO(ERROR_INVALID_PARAM,
"State is NULL");
202 if (state->
current_state != CONSENSUS_STATE_ELECTION_COMPLETE) {
203 return SET_ERRNO(ERROR_INVALID_STATE,
"Invalid transition from state %d", state->
current_state);
212 return CONSENSUS_STATE_FAILED;
218 if (!state || !out_host_id) {
219 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters");
222 if (state->
current_state != CONSENSUS_STATE_ELECTION_COMPLETE) {
223 return SET_ERRNO(ERROR_INVALID_STATE,
"Election not complete, current state: %d", state->
current_state);
231 if (!state || !out_backup_id) {
232 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters");
235 if (state->
current_state != CONSENSUS_STATE_ELECTION_COMPLETE) {
236 return SET_ERRNO(ERROR_INVALID_STATE,
"Election not complete, current state: %d", state->
current_state);
258 participant_metrics_t *out_metrics) {
259 if (!state || !out_metrics || index < 0) {
260 return SET_ERRNO(ERROR_INVALID_PARAM,
"Invalid parameters");
264 return SET_ERRNO(ERROR_INVALID_PARAM,
"Index %d out of bounds (count: %d)", index, state->
metrics_count);
269 state->
current_state != CONSENSUS_STATE_COLLECTION_COMPLETE &&
272 return SET_ERRNO(ERROR_INVALID_STATE,
"Cannot retrieve metrics in state %d", state->
current_state);
275 memcpy(out_metrics, &state->
metrics[index],
sizeof(participant_metrics_t));
asciichat_error_t consensus_state_start_collection(consensus_state_t *state)
asciichat_error_t consensus_state_compute_election(consensus_state_t *state)
asciichat_error_t consensus_state_get_metric_at(const consensus_state_t *state, int index, participant_metrics_t *out_metrics)
bool consensus_state_is_leader(const consensus_state_t *state)
asciichat_error_t consensus_state_get_elected_backup(const consensus_state_t *state, uint8_t out_backup_id[16])
void consensus_state_destroy(consensus_state_t *state)
asciichat_error_t consensus_state_get_elected_host(const consensus_state_t *state, uint8_t out_host_id[16])
asciichat_error_t consensus_state_reset_to_idle(consensus_state_t *state)
asciichat_error_t consensus_state_collection_complete(consensus_state_t *state)
struct consensus_state consensus_state_t
State machine instance.
int consensus_state_get_metrics_count(const consensus_state_t *state)
asciichat_error_t consensus_state_create(const uint8_t my_id[16], const consensus_topology_t *topology, consensus_state_t **out_state)
consensus_state_machine_t consensus_state_get_current_state(const consensus_state_t *state)
asciichat_error_t consensus_state_add_metrics(consensus_state_t *state, const participant_metrics_t *metrics)
election_result_t election_result
participant_metrics_t * metrics
consensus_topology_t * topology
consensus_state_machine_t current_state
Ring topology for consensus participants.
bool consensus_topology_am_leader(const consensus_topology_t *topology)
uint64_t time_get_realtime_ns(void)