Esempio n. 1
0
/* Must be called after tpclog_iterate_begin has been called on LOG. Attempts
 * to return the next most recent entry after the entry previously returned
 * during the current iteration, or, immediately after tpclog_iterate_begin,
 * the oldest entry in LOG, using malloc()d memory which should later be
 * free()d. Returns NULL if there is an error or no more recent entry exists
 * (i.e., all entries have been iterated over). */
logentry_t *tpclog_iterate_next(tpclog_t *log, logentry_t *entry) {
  char filename[MAX_FILENAME];
  int ret;
  pthread_rwlock_rdlock(&log->lock);
  if (!tpclog_iterate_has_next(log)) {
    pthread_rwlock_unlock(&log->lock);
    return NULL;
  }
  sprintf(filename, "%s/%lu%s", log->dirname, log->iterpos++, TPCLOG_FILETYPE);
  ret = tpclog_load_entry(entry, filename);
  pthread_rwlock_unlock(&log->lock);
  return (ret < 0) ? NULL : entry;
}
Esempio n. 2
0
/* Restore SERVER back to the state it should be in, according to the
 * associated LOG. Must be called on an initialized SERVER. Only restores the
 * state of the most recent TPC transaction, assuming that all previous actions
 * have been written to persistent storage. Should restore SERVER to its exact
 * state; e.g. if SERVER had written into its log that it received a PUTREQ but
 * no corresponding COMMIT/ABORT, after calling this function SERVER should
 * again be waiting for a COMMIT/ABORT.  This should also ensure that as soon
 * as a server logs a COMMIT, even if it crashes immediately after (before the
 * KVStore has a chance to write to disk), the COMMIT will be finished upon
 * rebuild. The cache need not be the same as before rebuilding.
 *
 * Checkpoint 2 only. */
int kvserver_rebuild_state(kvserver_t *server) {
  if (server == NULL || server->state == TPC_INIT) {
    return -1;
  }
  tpclog_iterate_begin(&server->log);
  logentry_t *prev = NULL, *next = NULL;
  while (tpclog_iterate_has_next(&server->log)) {
    next = tpclog_iterate_next(&server->log);
    if (next->type == PUTREQ || next->type == DELREQ)
      prev = next;
  }
  if (prev == NULL && next == NULL) { // log was empty
    return 0;
  } else if (prev == NULL) {
    prev = next;
  }

  server->msg = (kvmessage_t *) malloc(sizeof(kvmessage_t));
  if (server->msg == NULL)
    return -1;

  if (next->type == COMMIT) {
    server->state = TPC_READY;
    if (prev->type == PUTREQ) {
      if (rebuild_kvmessage(server, prev, true) == -1) {
        return -1;
      }
      kvserver_put(server, server->msg->key, server->msg->value);
    } else if (prev->type == DELREQ) {
      if (rebuild_kvmessage(server, prev, false) == -1) {
        return -1;
      }
      kvserver_del(server, server->msg->key);
    }
  } else if (next->type == ABORT) { // might want to avoid commiting very first time with a one-time use bool
    server->state = TPC_READY;
    if (prev != NULL && rebuild_kvmessage(server, prev, prev->type == PUTREQ) == -1)
      return -1;
  } else {
    server->state = TPC_WAIT;
    if (rebuild_kvmessage(server, next, next->type == PUTREQ) == -1)
      return -1;
  }
  return tpclog_clear_log(&server->log);
}
Esempio n. 3
0
/* Handles an incoming kvrequest REQ, and populates RES as a response.  REQ and
 * RES both must point to valid kvrequest_t and kvrespont_t structs,
 * respectively. Assumes that the request should be handled as a TPC
 * message. This should also log enough information in the server's TPC log to
 * be able to recreate the current state of the server upon recovering from
 * failure. See the spec for details on logic and error messages.
 */
void tpcfollower_handle_tpc(tpcfollower_t *server, kvrequest_t *req, kvresponse_t *res) {
  /* TODO: Implement me! */

  if (req->type == DELREQ) {
    if (server->state != TPC_WAIT) {
      server->state = TPC_WAIT;
    } else {
      res->type = ERROR;
      strcpy(res->body, ERRMSG_INVALID_REQUEST);
      return;
    }

    int del_retval = tpcfollower_del_check(server, req->key);
    if (del_retval < 0) {
      res->type = VOTE;
      if (del_retval == ERR_KEYLEN) {
        strcpy(res->body, ERRMSG_KEY_LEN);
      } else if (del_retval == ERR_NOKEY) {
        strcpy(res->body, ERRMSG_NO_KEY);
      } else {
        strcpy(res->body, ERRMSG_GENERIC_ERROR);
      }
    } else {
      tpclog_log(&server->log, DELREQ, req->key, NULL);
      res->type = VOTE;
      strcpy(res->body, MSG_COMMIT);

    }
  } else if (req->type == PUTREQ) {
    if (server->state != TPC_WAIT) {
      server->state = TPC_WAIT;
    } else {
      res->type = ERROR;
      strcpy(res->body, ERRMSG_INVALID_REQUEST);
      return;
    }

    int put_retval = tpcfollower_put_check(server, req->key, req->val);
    if (put_retval < 0) {
      res->type = VOTE;
      if (put_retval == ERR_KEYLEN) {
        strcpy(res->body, ERRMSG_KEY_LEN);
      } else if (put_retval == ERR_VALLEN) {
        strcpy(res->body, ERRMSG_VAL_LEN);
      } else {
        strcpy(res->body, ERRMSG_GENERIC_ERROR);
      }
    } else {
      tpclog_log(&server->log, PUTREQ, req->key, req->val);
      res->type = VOTE;
      strcpy(res->body, MSG_COMMIT);
    }
  } else if (req->type == COMMIT) {
    server->state = TPC_COMMIT;

    res->type = ACK;
    tpclog_iterate_begin(&server->log);
    logentry_t* entry = malloc(sizeof(logentry_t));

    while (tpclog_iterate_has_next(&server->log)) {
      entry = tpclog_iterate_next(&server->log, entry);
      char* log_key = malloc(strlen(entry->data));

      if (entry->type == DELREQ) {
        strcpy(log_key, entry->data);

        tpcfollower_del(server, log_key);
      } else if (entry->type == PUTREQ) {
        char* log_val = malloc(entry->length - strlen(entry->data));
        strcpy(log_key, entry->data);
        strcpy(log_val, entry->data + strlen(entry->data) + 1);

        tpcfollower_put(server, log_key, log_val);
      }
    } 

    tpclog_clear_log(&server->log);

  } else if (req->type == ABORT) {
    server->state = TPC_ABORT;

    res->type = ACK;
    tpclog_clear_log(&server->log);
  } else if (req->type == GETREQ) {
    int get_retval = tpcfollower_get(server, req->key, res->body);
    if (get_retval < 0) {
      res->type = ERROR;
      if (get_retval == ERR_KEYLEN) {
        strcpy(res->body, ERRMSG_KEY_LEN);
      } else if (get_retval == ERR_NOKEY) {
        strcpy(res->body, ERRMSG_NO_KEY);
      } else {
        strcpy(res->body, ERRMSG_INVALID_REQUEST);
      }
    } else {
      res->type = GETRESP;
    }
  }


}