/** * acceptor_reject - Notify the proposer that we reject their decree. */ int acceptor_reject(struct paxos_header *hdr) { int r; struct yakyak yy; // Pack a header. hdr->ph_opcode = OP_REJECT; yakyak_init(&yy, 1); paxos_header_pack(&yy, hdr); // Send the payload. r = paxos_send_to_proposer(&yy); yakyak_destroy(&yy); return r; }
/** * acceptor_last - Send the instance number of our last contiguous learn to * the proposer. */ int acceptor_last(struct paxos_header *hdr) { int r; struct yakyak yy; // Modify the header opcode. hdr->ph_opcode = OP_LAST; // Pack and send the response. yakyak_init(&yy, 2); paxos_header_pack(&yy, hdr); paxos_paxid_pack(&yy, pax->ihole - 1); r = paxos_send_to_proposer(&yy); yakyak_destroy(&yy); return r; }
/** * continue_ack_refuse - If we were able to reestablish connection with the * purported proposer, reset our proposer and reintroduce ourselves. */ int do_continue_ack_refuse(GIOChannel *chan, struct paxos_acceptor *acc, struct paxos_continuation *k) { int r = 0; struct paxos_header hdr; struct paxos_request *req; struct yakyak yy; // If we are the proposer and have finished preparing, anyone higher-ranked // than we are is dead to us. However, their parts may not yet have gone // through, so we make sure to ignore attempts at reconnection. if (is_proposer() && pax->prep == NULL) { return 0; } // Register the reconnection. acc->pa_peer = paxos_peer_init(chan); if (acc->pa_peer != NULL) { // Account for a new acceptor. pax->live_count++; // Free any prep we have. Although we dispatch as an acceptor when we // acknowledge a refuse, when the acknowledgement continues here, we may // have become the proposer. Thus, if we are preparing, we should just // give up. If the acceptor we are reconnecting to fails, we'll find // out about the drop and then reprepare. g_free(pax->prep); pax->prep = NULL; instance_container_destroy(&pax->idefer); // Say hello. ERR_ACCUM(r, paxos_hello(acc)); if (acc->pa_paxid < pax->proposer->pa_paxid) { // Update the proposer only if we have not reconnected to an even // higher-ranked acceptor. pax->proposer = acc; // Resend our request. // XXX: What about the problematic case where A is connected to B, B // thinks it's the proposer and accepts A's request, but in fact B is not // the proposer and C, the real proposer, gets neither of their requests? header_init(&hdr, OP_REQUEST, pax->proposer->pa_paxid); req = request_find(&pax->rcache, k->pk_data.req.pr_val.pv_reqid); if (req == NULL) { req = &k->pk_data.req; } yakyak_init(&yy, 2); paxos_header_pack(&yy, &hdr); paxos_request_pack(&yy, req); ERR_ACCUM(r, paxos_send_to_proposer(&yy)); yakyak_destroy(&yy); } } return r; }
/** * paxos_request - Request that the proposer make a decree for us. * * If the request has data attached to it, we broadcast an out-of-band message * to all acceptors, asking that they cache our message until the proposer * commits it. * * We send the request as a header along with a two-object array consisting * of a paxos_value (itself an array) and a msgpack raw (i.e., a data * string). */ int paxos_request(struct paxos_session *session, dkind_t dkind, const void *msg, size_t len) { int r, needs_cached; struct paxos_header hdr; struct paxos_request *req; struct paxos_yak py; // Set the session. The client should pass us a pointer to the correct // session object which we returned when the session was created. pax = session; // We can't make requests if we're not part of a protocol. if (pax == NULL) { return 1; } // Do we need to cache this request? needs_cached = request_needs_cached(dkind); // Initialize a header. We overload ph_inum to the ID of the acceptor who // we believe to be the proposer. header_init(&hdr, OP_REQUEST, pax->proposer->pa_paxid); // Allocate a request and initialize it. req = g_malloc0(sizeof(*req)); req->pr_val.pv_dkind = dkind; req->pr_val.pv_reqid.id = pax->self_id; req->pr_val.pv_reqid.gen = (++pax->req_id); // Increment our req_id. req->pr_val.pv_extra = 0; // Always 0 for requests. req->pr_size = len; req->pr_data = g_memdup(msg, len); // Add it to the request cache if needed. if (needs_cached) { request_insert(&pax->rcache, req); } if (!is_proposer() || needs_cached) { // We need to send iff either we are not the proposer or the request // has nontrivial data. paxos_payload_init(&py, 2); paxos_header_pack(&py, &hdr); paxos_request_pack(&py, req); // Broadcast only if it needs caching. if (!needs_cached) { r = paxos_send_to_proposer(&py); } else { r = paxos_broadcast(&py); } paxos_payload_destroy(&py); if (r) { return r; } } // Decree the request if we're the proposer; otherwise just return. if (is_proposer()) { return proposer_decree_request(req); } else { return 0; } }