Collaboration policy // Submit lab // Setup Go // Guidance // Piazza
In this lab you will build a key/value server for a single machine that ensures that each operation is executed exactly once despite network failures and that the operations are linearizable. Later labs will replicate a server like this one to handle server crashes.
Clients can send three different RPCs to the key/value server: Put(key, value), Append(key, arg), and Get(key). The server maintains an in-memory map of key/value pairs. Keys and values are strings. Put(key, value) installs or replaces the value for a particular key in the map, Append(key, arg) appends arg to key's value and returns the old value, and Get(key) fetches the current value for the key. A Get for a non-existent key should return an empty string. An Append to a non-existent key should act as if the existing value were a zero-length string. Each client talks to the server through a Clerk with Put/Append/Get methods. A Clerk manages RPC interactions with the server.
Your server must arrange that application calls to Clerk Get/Put/Append methods be linearizable. If client requests aren't concurrent, each client Get/Put/Append call should observe the modifications to the state implied by the preceding sequence of calls. For concurrent calls, the return values and final state must be the same as if the operations had executed one at a time in some order. Calls are concurrent if they overlap in time: for example, if client X calls Clerk.Put(), and client Y calls Clerk.Append(), and then client X's call returns. A call must observe the effects of all calls that have completed before the call starts.
Linearizability is convenient for applications because it's the behavior you'd see from a single server that processes requests one at a time. For example, if one client gets a successful response from the server for an update request, subsequently launched reads from other clients are guaranteed to see the effects of that update. Providing linearizability is relatively easy for a single server.
We supply you with skeleton code and tests in src/kvsrv. You will need to modify kvsrv/client.go, kvsrv/server.go, and kvsrv/common.go.
To get up and running, execute the following commands. Don't forget the git pull to get the latest software.
$ cd ~/6.5840 $ git pull ... $ cd src/kvsrv $ go test ... $
Your first task is to implement a solution that works when there are no dropped messages.
You'll need to add RPC-sending code to the Clerk Put/Append/Get methods in client.go, and implement Put, Append() and Get() RPC handlers in server.go.
You have completed this task when you pass the first two tests in the test suite: "one client" and "many clients".
Add code to Clerk to retry if doesn't receive a reply, and to server.go to filter duplicates if the operation requires it. These notes include guidance on duplicate detection.
Your code should now pass all tests, like this:
$ go test Test: one client ... ... Passed -- t 3.8 nrpc 31135 ops 31135 Test: many clients ... ... Passed -- t 4.7 nrpc 102853 ops 102853 Test: unreliable net, many clients ... ... Passed -- t 4.1 nrpc 580 ops 496 Test: concurrent append to same key, unreliable ... ... Passed -- t 0.6 nrpc 61 ops 52 Test: memory use get ... ... Passed -- t 0.4 nrpc 4 ops 0 Test: memory use put ... ... Passed -- t 0.2 nrpc 2 ops 0 Test: memory use append ... ... Passed -- t 0.4 nrpc 2 ops 0 Test: memory use many puts ... ... Passed -- t 11.5 nrpc 100000 ops 0 Test: memory use many gets ... ... Passed -- t 12.2 nrpc 100001 ops 0 PASS ok 6.5840/kvsrv 39.000s
The numbers after each Passed are real time in seconds, number of RPCs sent (including client RPCs), and number of key/value operations executed (Clerk Get/Put/Append calls).