The functional goal of this assignment is to use Rust to implement a simple 2PC (two-phase commit) protocol.
The pedagogical goals of this assignment are two-fold:
In this assignment, you will write a two phase commit protocol that works with a single coordinator an arbitrary number of concurrent clients (issuing requests to the coordinator) and an arbitrary number of 2PC participants. To restrict the scope of the assignment, your solution may make the following assumptions:
./target/debug/two_phase_commit --help USAGE: two_phase_commit [OPTIONS] FLAGS: -h, --help Prints help information -V, --version Prints version information OPTIONS: -lSpecifies path to directory where logs are stored -m Mode: "run" starts 2PC, "client" starts a client process, "participant" starts a participant process, "check" checks logs produced by previous run -c Number of clients making requests -p Number of participants in protocol -r Number of requests made per client -s Probability participants successfully execute requests -S Probability participants successfully send messages -v Output verbosity: 0->No Output, 5->Output Everything --ipc_path Path for IPC socket for communication --num Participant / Client number for naming the log files. Ranges from 0 to num_clients - 1 or num_participants - 1
When you run the project in "run" mode, it should start the coordinator and launch the client and participant processes and store the commit logs in ./logs. Clients and participants are launched by spawning new children processes with the same arguments except in the "client" and "participant" mode respectively. In addition, the parent (coordinator) process will need to utilize the --ipc_path to set up IPC communication and the --num parameter to properly name the participant log files. When you run the project in "check" mode, it will analyze the commit logs produced by the last run to determine whether your 2PC protocol handled the specified number of client requests correctly.
We provide a skeleton implementation that should help get started and insulate you from some of the details of managing the rust build system. The skeleton code provided includes a specification for the cargo build tool (see below) and the simple module decomposition overviewed in the table below. The "Edits Needed?" Column indicates whether we expect you to need to write code in that file or not. The description column summarizes the module functionality and (if appropriate) the changes we expect you to make in that module.
File | Edits Needed? | Description |
---|---|---|
Cargo.toml | no | Build tool input, specifies dependences |
src/main.rs | yes | Starts application, required changes to launch clients, participants, coordinator. |
src/client.rs | yes | Implements client-side protocol and state tracking |
src/participant.rs | yes | Implements participant-side protocol and state tracking |
src/coordinator.rs | yes | Implements coordinator-side protocol and state tracking |
src/message.rs | maybe | Provides message format and related enums. |
src/tpcoptions.rs | maybe | Implements command line options and tracing initialization |
src/oplog.rs | no | Implements a commit log API to use in coordinator and participant |
src/checker.rs | no | Implements the correctness check for "check" mode. |
In general, the code in the skeleton is documented to be explicit about what functionality
must be implemented and where. For example, the following code fragment from src/main.rs
documents what the run_participant()
function should do, provides specifications
for each parameter, a handful of HINTS to keep in mind and an explicit block comment where
we expect you to need to implement something. While in most cases you have considerably more
design freedom than is illustrated by this example, you should find that the skeleton provides
reasonably complete documentation of what is expected in each module.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /// /// pub fn run_participant(opts: &tpcoptions:TPCOptions, running: Arc<AtomicBool>) /// opts: An options structure containing the CLI arguments /// running: An atomically reference counted (ARC) AtomicBool(ean) that is /// set to be false whenever Ctrl+C is pressed /// /// 1. Connects to the coordinator to get tx/rx /// 2. Constructs a new participant /// 3. Starts the participant protocol /// fn run_participant(opts: & tpcoptions::TPCOptions, running: Arc<AtomicBool>) { let participant_id_str = format!("participant_{}", opts.num); let participant_log_path = format!("{}//{}.log", opts.log_path, participant_id_str); // TODO } |
cargo build
When you build the skeleton code, you should get a functional executable right away. You will see a large number of warnings in the skeleton code, reflecting the fact that that the rust compiler does not like the unreferenced variables and modules left by removing logic from our solution to create the skeleton.
src/checker.rs
and is provided for you. If you use the message
codes and message types specified in src/message.rs
, the checker should "just work"
for you.
rossbach@moonstone> ./target/debug/two_phase_commit -s .95 -c 4 -p 10 -r 10 -m run CTRL-C! coordinator: C:26 A:14 U:0 participant_2: C:26 A:14 U:0 participant_1: C:26 A:14 U:0 participant_8: C:26 A:14 U:0 participant_3: C:26 A:14 U:0 participant_6: C:26 A:14 U:0 participant_0: C:26 A:14 U:0 participant_5: C:26 A:14 U:0 participant_9: C:26 A:14 U:0 participant_4: C:26 A:14 U:0 participant_7: C:26 A:14 U:0 rossbach@moonstone> ./target/debug/two_phase_commit -s .95 -c 4 -p 10 -r 10 -m check -v 0 participant_9 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_4 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_3 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_1 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_5 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_6 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_8 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_0 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_2 OK: C:26 == 26(C-global), A:14 <= 14(A-global) participant_7 OK: C:26 == 26(C-global), A:14 <= 14(A-global)
stderrlog
for you, so you
should have a rich set of tools for instrumenting your application to simplify debugging.
We have left many fragments of code that use trace!()
and info!()
to illustrate how to use them. You just need to vary the verbosity argument to set the
logging level.When you submit your solution, you should include the following:
Please report how much time you spent on the lab.