uint64_t log_and_send_append_entries(struct server_context_t *s, uint64_t req_id, unsigned char *buf, size_t bufsize) { DBG_LOG(LOG_DEBUG, "[%s][%d] logging request for client with req_id = %llu" , ss[s->state], s->current_term, req_id); assert(s->state == LEADER); struct log_entry_t *prev = get_last_entry(s->log); uint64_t cur_index = 1; if(prev) { cur_index = prev->index + 1; } //else this is first entry hence cur_index = 1 struct log_entry_t *cur = append_log_entry(s->log, s->current_term, cur_index, req_id, buf, bufsize); if(!cur) { return -1; } s->last_entry = cur; for(int i = 0; i < s->quoram_size - 1; i++) { send_append_entries(s, cur_index, i); } return cur_index; }
static void append_entries_cb(struct data_t *result, char *err, void *data) { /* TODO: why we need to create and then free the data, cant we just * reuse the slots, especially in case of heartbeat message? */ struct append_entries_cb_data_t *cb_data = (struct append_entries_cb_data_t *)data; struct server_context_t *s = cb_data->s; int peer_index = cb_data->index; uint64_t first_index = cb_data->first_index; uint64_t last_index = cb_data->last_index; free(cb_data); if(err) { //LOG: fatal error. TODO: exit? DBG_LOG(LOG_DEBUG, "[%s][%d] FAIL append_entries_cb from peer_id = %d with err = %s" , ss[s->state], s->current_term, peer_index, err); return; } struct append_entries_output_t *res = get_append_entries_output(result); if(!res) { log_fatal_and_exit(s, "AppendEntriesCallback: Response parsing failed"); return; } uint64_t pair_term = res->term; int pair_success = res->success; free(res); if(pair_term > s->current_term) { set_term(s, pair_term); s->voted_for = 0; save_state(s); return; } if(s->state != LEADER) { return; } if(!pair_success) { DBG_LOG(LOG_DEBUG, "[%s][%d] FAIL append_entries_cb from peer_id = %d" , ss[s->state], s->current_term, peer_index); //TODO: send the client prev entry s->next_index[peer_index] = first_index - 1; } else { DBG_LOG(LOG_DEBUG, "[%s][%d] OK append_entries_cb from peer_id = %d" , ss[s->state], s->current_term, peer_index); s->next_index[peer_index] = last_index + 1; s->match_index[peer_index] = last_index; //If there exists an N such that N > commitIndex, a majority //of matchIndex[i] ≥ N, and log[N].term == currentTerm: //set commitIndex = N (§5.3, §5.4). if(s->match_index[peer_index] > s->commit_index) { int nOK[s->quoram_size - 1]; for(int i = 0; i < s->quoram_size - 1; i++) { nOK[i] = 0; } for(int i = 0; i < s->quoram_size - 1; i++) { for(int j = 0; j < s->quoram_size - 1; j++) { if(s->match_index[i] <= s->match_index[j]) { nOK[i]++; } } } int majority = (s->quoram_size/2); uint64_t N = s->commit_index; int minOKs = s->quoram_size; for(int i = 0; i < s->quoram_size - 1; i++) { if(nOK[i] >= majority && nOK[i] < minOKs) { minOKs = nOK[i]; if(s->match_index[i] > N) { N = s->match_index[i]; } } } if(N > s->commit_index) { struct log_entry_t *entryN = get_log_entry_at(s->log, N); //never commit entry from previous term (§5.4.2) if(entryN && (entryN->term == s->current_term)) { s->commit_index = N; commit_unapplied_entries(s, N); } } } } if(s->last_entry && s->last_entry->index >= s->next_index[peer_index]) { //send all the entries since next_index till some threshold //to this server if(send_append_entries(s, s->next_index[peer_index], peer_index)) { //LOG: fatal error. TODO: exit? } } }
bool gallocy::consensus::GallocyClient::send_append_entries() { gallocy::vector<LogEntry> empty; return send_append_entries(empty); }