416 Distributed Systems: Assignment 7Due: Apr 14 at midnight2016W2, Winter 2017 |
In this assignment you will create a different implementation of the A6 transactional key-value service that has a nearly identical interface and semantics. Your A7 implementation will be based on a distributed block-chain. High-level descriptionAs in A6, your system will be composed of N nodes and some clients. Your system must provide identical fault tolerance guarantees: nodes can fail and your system must be available even if N-1 of the nodes fail. As before, when nodes fail, they do not come back. Similarly to A6, clients may also fail, and your system must be able to continue as these failures occur. Your service must implement the key-value service as a distributed block-chain that is distributed among, and known by, all of the nodes. Blocks in this block-chain will correspond to client transactions. Nodes will non-deterministically race to integrate transactions into the block-chain using a proof-of-work approach based on Hashcash. Your system should have the following structure:
As in A6 your system must provide transactional (ACID) semantics to clients without Durability. In contrast with A6, you are not allowed to abort a transaction when nodes fail. Non-conflict transactions (that do not overlap in the keys accessed sets) should be able to commit successfully regardless of node failures. Conflicting transactions (that overlap in the keys accessed sets) should execute without blocking (as with optimistic concurrency control), but may abort on an operation when a conflicting transaction has been integrated into the block chain (but not necessarily validated). The detailsBlock generation. Each node must implement a mining procedure by which it can generate a new block in the block chain. A node can only compute one block at a time and cannot work on multiple blocks simultaneously. There are two kinds of blocks: no-op blocks and txn blocks.
Block chain. Nodes maintain a tree representation of the block chain. The chain is the longest path in this tree, starting at the genesis block whose hash is specified on the node's command line. A node should only compute no-op and txn blocks along the chain, and not along any shorter path in the tree. In the case that there are several (longest) chains, the node should (1) pick the one that does not cause a transaction abort for the current txn block it is generating, or if no txn block is being generated or none cause an abort for the existing transaction, then (2) pick among the chains uniformly at random. Block data structure. A txn block is a data structure that contains at least the following data:
Note that a block hash computed for a txn or a no-op by a node A will always be different from blocks generated by other nodes (for the same txn/no-op) and will always differ from other block generated by node A. This is because a block contains a prev-hash, which uniquely identifies its position in the tree, and a block contains a node ID (e.g., of node A), which makes each block unique to A. A no-op block is identical to a txn block except that it does not include a txn. Its hash is similarly computed using a proof-of-work algorithm. Note that a txn block only needs to record the mutating operations in a transaction (i.e., put commands). That is, a transaction in the txn block can be represented as a key-value map that is a subset of the global key-value map, and which contains updated values for those keys that were mutated by put operations in the transaction. Key-value API. Each node must implement the key-value interface from A6. For this, the node must (1) maintain the key-value store that corresponds to the in-order execution of all the transactions along the block-chain, and (2) it must respond to get/put requests against this version of the key-value store. It must continue to respond to client queries even though the block-chain underneath changes. As noted above, a node may respond with an abort on an operation in the case that a conflicting transaction has been integrated into the block-chain. As in A6, an aborted transaction must remain aborted and can never commit. For this assignment an outstanding transaction conflicts with a transaction that is part of the block chain if the set of accessed keys by the outstanding transaction overlaps with the keys accessed by puts in the transaction in the block chain. Applications will use a modified A6-interface to operate on your key-value store. As in A6 you are not allowed to change this client interface. However, you have design freedom to implement this interface however you want.
Semantic difference between A6 and A7. Although A6 and A7 provide a nearly identical API and have very similar semantics, there are cases in which A7 semantics will differ from A6. Here are a few such cases Assume you have a chain that looks like: Genesis Block <- Block 1{TX1, validateNum: 6} <- Block 2{TX2, validateNum: 1}. Then, once TX2 is validated, the client for TX2 should get their commit returned regardless of whether TX1 has been validated. This has two outcomes:
Assumptions you can makeIdentical to A6:
Assumptions you cannot makeIdentical to A6:
Implementation requirements
Extra creditThe assignment is extensible with extra credit. You must create an EXTRACREDIT.txt file in your repository and specify in this file which extra credit features you have implemented, one per line, e.g., EC1. EC1 (3% of final grade): A benefit of a proof-of-work design is that it makes it challenging to mount Sybil attacks. However, a majority of nodes can still collude to disrupt the system. Create a malicious node (malicious-kvnode.go) that takes identical arguments to a regular node and mounts a denial of service attack against your system by sneaking in spurious transactions into the block-chain that cause real client transactions to abort. Your malicious nodes should behave like regular nodes when a minority (< 50%) of nodes are malicious. When a majority (> 50%) of nodes in the system are malicious, the malicious nodes should activate and behave maliciously, attempting to abort every received client transaction. EC1 will be tested without node failures. Your malicious nodes should abort client transactions with a probability proportional to the number of participating malicious nodes. EC2 (2% of final grade): Add support for adding new nodes to the system. You have to do this without changing any of the existing interfaces. The new node will be given a list of all the nodes that have ever existed in the system (regardless of whether or not they are currently alive). Once a new node has joined, it should print out "JOINED-SYSTEM\n" to stdout. At this point it should be safe to terminate all nodes except for the newly joined node and have the system continue to operate without unavailability. Solution specYou must submit kvnode.go, your version of kvservice.go, and any other go files your service implementation requires. Clients should be able to use your library by (1) setting GOPATH to the root of your repository, and (2) using import "./kvservice" at top of file. The kvnode process command line usage must be: go run kvnode.go [ghash] [num-zeroes] [nodesFile] [nodeID][listen-node-in IP:port] [listen-client-in IP:port]
Rough grading rubric
Make sure to follow the course collaboration policy and refer to the assignments instructions that detail how to submit your solution. |
|