Пример #1
0
Future<Option<uint64_t> > CoordinatorProcess::checkPromisePhase(
    const PromiseResponse& response)
{
  if (!response.okay()) {
    // Lost an election, but can be retried. We save the proposal
    // number here so that most likely we will have a high enough
    // proposal number when we retry.
    CHECK_LE(proposal, response.proposal());
    proposal = response.proposal();

    return None();
  } else {
    CHECK(response.has_position());
    index = response.position();

    // Need to "catch-up" local replica (i.e., fill in any unlearned
    // and/or missing positions) so that we can do local reads.
    // Usually we could do this lazily, however, a local learned
    // position might have been truncated, so we actually need to
    // catch-up the local replica all the way to the end of the log
    // before we can perform any up-to-date local reads.
    return getMissingPositions()
      .then(defer(self(), &Self::catchupMissingPositions, lambda::_1))
      .then(defer(self(), &Self::updateIndexAfterElected));
   }
}
Пример #2
0
TEST(ReplicaTest, Promise)
{
  const std::string path = utils::os::getcwd() + "/.log";

  utils::os::rmdir(path);

  Replica replica(path);

  PromiseRequest request;
  PromiseResponse response;
  Future<PromiseResponse> future;

  request.set_id(2);

  future = protocol::promise(replica.pid(), request);

  future.await(2.0);
  ASSERT_TRUE(future.isReady());

  response = future.get();
  EXPECT_TRUE(response.okay());
  EXPECT_EQ(2, response.id());
  EXPECT_TRUE(response.has_position());
  EXPECT_EQ(0, response.position());
  EXPECT_FALSE(response.has_action());

  request.set_id(1);

  future = protocol::promise(replica.pid(), request);

  future.await(2.0);
  ASSERT_TRUE(future.isReady());

  response = future.get();
  EXPECT_FALSE(response.okay());
  EXPECT_EQ(1, response.id());
  EXPECT_FALSE(response.has_position());
  EXPECT_FALSE(response.has_action());

  request.set_id(3);

  future = protocol::promise(replica.pid(), request);

  future.await(2.0);
  ASSERT_TRUE(future.isReady());

  response = future.get();
  EXPECT_TRUE(response.okay());
  EXPECT_EQ(3, response.id());
  EXPECT_TRUE(response.has_position());
  EXPECT_EQ(0, response.position());
  EXPECT_FALSE(response.has_action());

  utils::os::rmdir(path);
}
Пример #3
0
  void received(const PromiseResponse& response)
  {
    if (response.has_type() && response.type() ==
        PromiseResponse::IGNORED) {
      ignoresReceived++;

      // A quorum of replicas have ignored the request.
      if (ignoresReceived >= quorum) {
        LOG(INFO) << "Aborting explicit promise request because "
                  << ignoresReceived << " ignores received";

        // If the "type" is PromiseResponse::IGNORED, the rest of the
        // fields don't matter.
        PromiseResponse result;
        result.set_type(PromiseResponse::IGNORED);

        promise.set(result);
        terminate(self());
      }

      return;
    }

    responsesReceived++;

    if (isRejectedPromise(response)) {
      // Failed to get the promise from a replica for this position
      // because it has been promised to a proposer with a higher
      // proposal number. The 'proposal' field in the response
      // specifies the proposal number. It is found to be larger than
      // the proposal number used in this phase.
      if (highestNackProposal.isNone() ||
          highestNackProposal.get() < response.proposal()) {
        highestNackProposal = response.proposal();
      }
    } else if (highestNackProposal.isSome()) {
      // We still want to wait for more potential NACK responses so we
      // can return the highest proposal number seen but we don't care
      // about any more ACK responses.
    } else {
      // The position has been promised to us so the 'proposal' field
      // should match the proposal we sent in the request.
      CHECK_EQ(response.proposal(), request.proposal());

      if (response.has_action()) {
        CHECK_EQ(response.action().position(), position);
        if (response.action().has_learned() && response.action().learned()) {
          // Received a learned action. Note that there is no checking
          // that we get the _same_ learned action in the event we get
          // multiple responses with learned actions, we just take the
          // "first". In fact, there is a specific instance in which
          // learned actions will NOT be the same! In this instance,
          // one replica may return that the action is a learned no-op
          // because it knows the position has been truncated while
          // another replica (that hasn't learned the truncation yet)
          // might return the actual action at this position. Picking
          // either action is _correct_, since eventually we know this
          // position will be truncated. Fun!
          // TODO(neilc): Create a test case for this scenario.
          promise.set(response);

          // The remaining responses will be discarded in 'finalize'.
          terminate(self());
          return;
        } else if (response.action().has_performed()) {
          // An action has already been performed in this position, we
          // need to save the action with the highest proposal number.
          if (highestAckAction.isNone() ||
              (highestAckAction->performed() < response.action().performed())) {
            highestAckAction = response.action();
          }
        } else {
          // Received a response for a position that had previously
          // been promised to some other proposer but an action had
          // not been performed or learned. The position is now
          // promised to us. No need to do anything here.
        }
      } else {
        // Received a response without an action associated with it.
        // This is the case when this proposer is the first to request
        // a promise for this log position.
        CHECK(response.has_position());
        CHECK_EQ(response.position(), position);
      }
    }

    if (responsesReceived >= quorum) {
      // A quorum of replicas have replied.
      PromiseResponse result;

      if (highestNackProposal.isSome()) {
        result.set_type(PromiseResponse::REJECT);
        result.set_okay(false);
        result.set_proposal(highestNackProposal.get());
      } else {
        result.set_type(PromiseResponse::ACCEPT);
        result.set_okay(true);
        if (highestAckAction.isSome()) {
          result.mutable_action()->CopyFrom(highestAckAction.get());
        }
      }

      promise.set(result);
      terminate(self());
    }
  }
Пример #4
0
TEST_F(ReplicaTest, Promise)
{
  const string path = os::getcwd() + "/.log";
  initializer.flags.path = path;
  initializer.execute();

  Replica replica(path);

  PromiseRequest request;
  PromiseResponse response;
  Future<PromiseResponse> future;

  request.set_proposal(2);

  future = protocol::promise(replica.pid(), request);

  AWAIT_READY(future);

  response = future.get();
  EXPECT_TRUE(response.okay());
  EXPECT_EQ(2u, response.proposal());
  EXPECT_TRUE(response.has_position());
  EXPECT_EQ(0u, response.position());
  EXPECT_FALSE(response.has_action());

  request.set_proposal(1);

  future = protocol::promise(replica.pid(), request);

  AWAIT_READY(future);

  response = future.get();
  EXPECT_FALSE(response.okay());
  EXPECT_EQ(2u, response.proposal()); // Highest proposal seen so far.
  EXPECT_FALSE(response.has_position());
  EXPECT_FALSE(response.has_action());

  request.set_proposal(3);

  future = protocol::promise(replica.pid(), request);

  AWAIT_READY(future);

  response = future.get();
  EXPECT_TRUE(response.okay());
  EXPECT_EQ(3u, response.proposal());
  EXPECT_TRUE(response.has_position());
  EXPECT_EQ(0u, response.position());
  EXPECT_FALSE(response.has_action());
}