Details

Follower 2PC Handling

You should implement the 2PC follower to correctly handle normal GET and two-phase commit messages. Refer to Two Phase Commit for details on the implementation of two-phase commit logic. Refer to Testing for how to bypass the leader to communicate with followers directly.

Start by looking at the function HandleMessage in pkg/tpc/tpcfollower.go. This function is called every time the follower receives a message from the leader.

Based on what the message is, it will call either vote or global. You are responsible for filling in these two functions to update the follower state machine and return the appropriate response.

For this part, there is no need to implement any journaling or the replayJournal function.

Leader 2PC Handling

You should implement the 2PC leader to correctly handle normal GET and PUT requests. Refer to Two Phase Commit for details on the implementation of two-phase commit logic. The leader needs to send Leader messages to all of its followers as well as process the results from the followers. Refer to Testing for how to mock a follower for the Leader to communicate with.

Start by looking at the function Put . This is the function where all Two Phase Commit transactions begin. The purpose of this function is to update the leader state machine and send the correct messages to the followers. This function will call voteRequest and globalRequest.

voteRequest and globalRequest are responsible for sending the correct messages to the followers and parsing the response. Both functions call SendMessage, which returns a channel where the responses will be placed. You can expect there will eventually be numFollowers responses.

For voteRequest, if any of the responses are an ABORT then the function should return an ABORT. For globalRequest, it doesn't matter what each follower returns since the only thing that can be returned is an ACK. Rather, you should block until you receive numFollower ACKs.

One point of confusion here is what the SendMessage function does. SendMessage takes a LeaderMsg and sends it to every follower the leader is connected to. It returns a Golang channel of the responses and you can expect that there will eventually be numFollowers items in that channel.

Channels are a Golang feature that can be considered a built-in bounded queue. If the retry argument is set to True, then SendMessage will resend the same message to a follower that fails to respond within a timeout or responds with an error. If it is set to False, then such a follower will be considered to have responded with an ABORT by default. This may be useful in implementing the behavior of the Vote phases versus a Global phase.

Retransmitted Messages

To begin part B, you will need to go back and add functionality that allows the 2PC follower to handle the cases where the 2PC follower gets a retransmission of a previously seen message. For example, if the follower gets two COMMIT or ABORT messages in a row.

2PC Crash Handling

Now that you have the Leader and Follower 2PC implemented, you should be passing all the basic 2PC tests on the autograder as well as the End to End tests. Ideally, you also pass the retransmit tests as well. This is a good indicator that your internal state is correct -- something that is crucial to crash recovery.

Now you will implement the replayJournal function for both the Follower and the Leader. This function will iterate through the Journal and replay all the actions that have been committed to the journal. At the end, you should be able to continue receiving messages from where you left off in the Journal.

The journal can be read using an EntryIterator which as the functions HasNext and Next. The journal itself can be modified with the functions Append and Empty. You must implement journaling in the follower's vote and global functions. In the leader, you must implement journaling in Put.

In both, you should implement the replayJournal function. For the follower, this function should update the internal state based on what entries were logged. For the leader, this function should either make a global COMMIT or ABORT based on what was last logged.

When implementing replayJournal for the leader, you may at one point need to pass a context to some helper functions. The context is a concept in Golang that adds timeouts and deadlines to requests and you don't really need to understand how it's used. (Although if you are curious this video is great.) The context normally comes from the gRPC Put call, but when replaying the journal we no longer have a context to work with. Thus we need to make a new context, which you can do with context.Background().

Last updated