/** * continue_ack_reject - If we were able to reestablish connection, reintroduce * ourselves and redecree the attempted part as null. Otherwise, just try * decreeing the part again. */ int do_continue_ack_reject(GIOChannel *chan, struct paxos_acceptor *acc, struct paxos_continuation *k) { int r; struct paxos_instance *inst; // Obtain the rejected instance. If we can't find it, it must have been // sync'd away, so just return. inst = instance_find(&pax->ilist, k->pk_data.inum); if (inst == NULL) { return 0; } acc->pa_peer = paxos_peer_init(chan); if (acc->pa_peer != NULL) { // Account for a new live connection. pax->live_count++; // Reintroduce ourselves to the acceptor. ERR_RET(r, paxos_hello(acc)); // Nullify the instance. inst->pi_hdr.ph_opcode = OP_DECREE; inst->pi_val.pv_dkind = DEC_NULL; inst->pi_val.pv_extra = 0; } // Reset the instance metadata, marking one vote. instance_init_metadata(inst); // Decree null if the reconnect succeeded, else redecree the part. return paxos_broadcast_instance(inst); }
/** * paxos_ack_resend - Receive a resend of request data, and re-commit the * instance to which the request belongs. */ int paxos_ack_resend(struct paxos_header *hdr, msgpack_object *o) { struct paxos_instance *inst; struct paxos_request *req; // Grab the instance for which we wanted the request. If we find that it // has already been cached and learned since we began our retrieval, we can // just return. // // Note also that an instance should only be NULL if it was committed and // learned and then truncated in a sync operation. inst = instance_find(&pax->ilist, hdr->ph_inum); if (inst == NULL || inst->pi_cached) { return 0; } // See if we already got the request. It is possible that we received a // commit message for the instance before the original request broadcast // reached us. However, since the instance-request mapping is one way, we // wait until a resend is received before committing. req = request_find(&pax->rcache, inst->pi_val.pv_reqid); if (req == NULL) { // Allocate a request and unpack it. req = g_malloc0(sizeof(*req)); paxos_request_unpack(req, o); // Insert it to our request cache. request_insert(&pax->rcache, req); } // Commit again, now that we have the associated request. return paxos_commit(inst); }
/** * proposer_ack_reject - Acknowledge an acceptor's reject. * * Increment the reject count of the appropriate Paxos instance. If we have * a majority of rejects, try to reconnect to the acceptor we attempted to * force part. If we are successful, re-decree null; otherwise, try the part * again. */ int proposer_ack_reject(struct paxos_header *hdr) { int r; struct paxos_instance *inst; struct paxos_acceptor *acc; struct paxos_continuation *k; // Our prepare succeeded, so we have only one possible ballot in our // lifetime in the system. assert(ballot_compare(hdr->ph_ballot, pax->ballot) == 0); // Find the decree of the correct instance and increment the reject count. inst = instance_find(&pax->ilist, hdr->ph_inum); inst->pi_rejects++; // Ignore the vote if we've already committed. if (inst->pi_committed) { return 0; } // We only reject parts. However, we may continue to receive rejects even // after a majority rejects, in which case we may have re-decreed null. if (inst->pi_val.pv_dkind == DEC_NULL) { return 0; } assert(inst->pi_val.pv_dkind == DEC_PART); // If we have been rejected by a majority, attempt reconnection. if (DEATH_ADJUSTED(inst->pi_rejects) >= majority()) { // See if we can reconnect to the acceptor we tried to part. acc = acceptor_find(&pax->alist, inst->pi_val.pv_extra); assert(acc->pa_peer == NULL); // Defer computation until the client performs connection. If it succeeds, // replace the part decree with a null decree; otherwise, just redecree // the part. We bind the instance number of the decree as callback data. k = continuation_new(continue_ack_reject, acc->pa_paxid); k->pk_data.inum = inst->pi_hdr.ph_inum; ERR_RET(r, state.connect(acc->pa_desc, acc->pa_size, &k->pk_cb)); return 0; } // If we have heard back from everyone but the accepts and rejects are tied, // just decree the part again. if (inst->pi_votes < majority() && DEATH_ADJUSTED(inst->pi_rejects) < majority() && inst->pi_votes + inst->pi_rejects == pax->live_count) { return paxos_broadcast_instance(inst); } return 0; }