Exemple #1
0
/**
 * paxos_retrieve - Ask the originator of request data to send us data which
 * we do not have in our cache.
 *
 * We call this function when and only when we are issued a commit for an
 * instance whose associated request is not in our request cache.
 */
int
paxos_retrieve(struct paxos_instance *inst)
{
  int r;
  struct paxos_header hdr;
  struct paxos_acceptor *acc;
  struct paxos_yak py;

  // Initialize a header.  We set ph_inum to the instance number of the
  // request.
  header_init(&hdr, OP_RETRIEVE, inst->pi_hdr.ph_inum);

  // Pack the retrieve.
  paxos_payload_init(&py, 2);
  paxos_header_pack(&py, &hdr);
  paxos_payload_begin_array(&py, 2);
  paxos_paxid_pack(&py, pax->self_id);
  paxos_value_pack(&py, &inst->pi_val);

  // Determine the request originator and send.  If we are no longer connected
  // to the request originator, broadcast the retrieve instead.
  acc = acceptor_find(&pax->alist, inst->pi_val.pv_reqid.id);
  if (acc == NULL || acc->pa_peer == NULL) {
    r = paxos_broadcast(&py);
  } else {
    r = paxos_send(acc, &py);
  }
  paxos_payload_destroy(&py);

  return r;
}
Exemple #2
0
/**
 * proposer_sync - Send a sync command to all acceptors.
 *
 * For a sync to succeed, all acceptors need to tell us the instance number
 * of their last contiguous learn.  We take the minimum of these values
 * and then command everyone to truncate everything before this minimum.
 */
int
proposer_sync()
{
  int r;
  struct paxos_header hdr;
  struct yakyak yy;

  // If we haven't finished preparing as the proposer, don't sync.
  if (pax->prep != NULL) {
    return 1;
  }

  // If not everyone is live, we should delay syncing.
  if (pax->live_count != LIST_COUNT(&pax->alist)) {
    return 1;
  }

  // If our local last contiguous learn is the same as the previous sync
  // point, we don't need to sync.
  if (pax->ihole - 1 == pax->sync_prev) {
    return 0;
  }

  // If we're already syncing, increment the skip counter.
  if (pax->sync != NULL) {
    // Resync if we've waited too long for the sync to finish.
    if (++pax->sync->ps_skips < SYNC_SKIP_THRESH) {
      return 1;
    } else {
      g_free(pax->sync);
    }
  }

  // Create a new sync.
  pax->sync = g_malloc0(sizeof(*(pax->sync)));
  pax->sync->ps_total = LIST_COUNT(&pax->alist);
  pax->sync->ps_acks = 1;  // Including ourselves.
  pax->sync->ps_skips = 0;
  pax->sync->ps_last = 0;

  // Initialize a header.
  header_init(&hdr, OP_SYNC, ++pax->sync_id);

  // Pack and broadcast the sync.
  yakyak_init(&yy, 1);
  paxos_header_pack(&yy, &hdr);
  r = paxos_broadcast(&yy);
  yakyak_destroy(&yy);

  return r;
}
Exemple #3
0
/**
 * proposer_truncate - Command all acceptors to drop the contiguous prefix
 * of Paxos instances for which every participant has learned.
 */
int
proposer_truncate(struct paxos_header *hdr)
{
  int r;
  struct yakyak yy;

  // Obtain our own last contiguous learn.
  if (pax->ihole - 1 < pax->sync->ps_last) {
    pax->sync->ps_last = pax->ihole - 1;
  }

  // Record the sync point.
  pax->sync_prev = pax->sync->ps_last;

  // Make this instance our new ibase; this ensures that our list always has
  // at least one committed instance.
  assert(pax->sync->ps_last >= pax->ibase);
  pax->ibase = pax->sync->ps_last;

  // Modify the header.
  hdr->ph_opcode = OP_TRUNCATE;

  // Pack a truncate.
  yakyak_init(&yy, 2);
  paxos_header_pack(&yy, hdr);
  paxos_paxid_pack(&yy, pax->ibase);

  // Broadcast it.
  r = paxos_broadcast(&yy);
  yakyak_destroy(&yy);
  if (r) {
    return r;
  }

  // Do the truncate (< pax->ibase).
  ilist_truncate_prefix(&pax->ilist, pax->ibase);

  // End the sync.
  g_free(pax->sync);
  pax->sync = NULL;

  return 0;
}
Exemple #4
0
/**
 * 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;
  }
}