CS 241

MP7: KeyValue


Introduction

You've just arrived at your new internshp at Microoglebook, Inc., and you've received your first project! Your fellow Microoglers have nearly completed work on their Real-Time Cross-Platform Big Data Mining Whosiwhatsit™, and they've left one big task up to you!

The RTCPFDMW™ requires a Key-Value data structure that can be accessed in parallel through an HTTP interface. All of the necessary piping for the server is done, and you are tasked with creating the data structure itself. Your data structure must not only be efficient in both memory usage and runtime (hint: constant time access would work wonders), but also be able to save and load itself from disk.

In order to handle race conditions, your data structure must use the concept of a "revision number." Whenever a request is made to get a key, you must provide the requester with the revision number of the key. To modify the value associated with a key, a requester must provide the data structure with the most updated revision number of the key.


Where's my score?

This MP is continuously graded. You results are posted here. It takes about 3-5 minutes (per student) between committing code and seeing your result on the result page. A reminder that there is a 2% bonus if you can beat PAULBOT 400

The MP

What we provide for you:

We provide you with two structs, a request struct and a response struct which include the following:

struct jsonreq_t
{
char* key;
char* value;
int rev;
}

struct jsonres_t
{
char* success;
char* value;
int rev;
}

What you must do for this MP:

Note that your solution must be self-contained in datastore_control.c, datastore_control.h, datastore.c, and datastore.h. That is to say, you shouldn't create your own files to write your data structure in, nor should you #include any other files within the mp7 folder.

You must implement the three functions in datastore_control.c:

jsonres_t process_request(const char * uri, jsonreq_t request)
void save()
void load()

process_request is the main function of the MP. It will handle all modifications to the state of your data structure. It is important to note that multiple threads may call this function at the same time, so your data structure must be able to handle concurrent requests from it. You may use datastore.h and datastore.c as a separate place to write your data structure. process_request takes two arguments: uri, which defines which action is being taken, and request, which holds the relevant data for the request. You must implement the functionalities for each of the following possible input values for uri:

For save(), you must take the current state of your data structure to disk by saving it as a file in the format of your choosing. We have provided you with a folder named data in which to store your data structure. Note that you may only store to disk within the data folder. Storing to any other locations will result in a 0% score on the MP.
After saving to disk, save must free all heap memory that you allocated. We may test this with valgrind.

For load(), you must initialized you data structure and restore it's state from the files you saved in the data folder. If the data folder is empty, assume the program is being run for the first time. Note that this is how your program will be tested; We will empty the data folder to "reset" your program inbetween tests.

You may assume that all process_request calls are between a load and save call. For clairifaction, you can look at test-1.c, ... , test-5.c in the testing folder, especially test-3.c.

Compile

As always, compile using:
make clean
make

Testing and Grading

To get 100% on this MP, you must pass all of the tests supplied in the testing folder and use memory correctly. You can run these tests by running the following:

cd testing
make clean
make

You have passed all the tests if you get the following output for make (worth 100%):

make --quiet quietrun
test-1: SUCCESS
test-2: SUCCESS
test-3: SUCCESS
test-4: SUCCESS
test-5: SUCCESS

You must also use memory correctly for these tests. You can verify that you use memory correctly with valgrind by running the following for [n] = 1,2,...,5:

rm -rf data
mkdir data
valgrind ./test-[n]

The output of each of these must contain the following for full credit:

All heap blocks were freed -- no leaks are possible
ERROR SUMMARY: 0 errors from 0 contexts

You can also test the end-to-end functionality of the RTCPFDMW™ by running the following in the mp7 directory:

make clean
make
./server [PORT]

Where [PORT] is a valid, open port. you can then send and receive JSON object from the server, formatting requests using the following formatting:

{
    "key": "[KEY]"
    "value": "[VALUE]"
    "revision": [REVISION]
}

Requests will be converted to a jsonreq_t with key "[KEY]", value "[VALUE]" and rev [REVISION]. You can make these requests with curl with the following format:

curl -H "Content-Type: application/json" --data '[REQUEST JSON]' http://localhost:[PORT]/[URI]

Here's an example add request for key "get", value "money" for a server running on port 3000:

curl -H "Content-Type: application/json" --data '{"key": "get", "value": "money"}' http://localhost:3000/add

Extra Credit Contest

You have the opportunity to earn 2% extra credit if you can beat a a solution made in 400 minutes by TA Paul. You results are posted here. The contest will be constantly updated much like the malloc contest starting later this week. Your score will be normalized with Paul's, with 6/7 of the score coming from runtime and 1/7 of the score coming from maximum memory usage. These numbers will be calculated using getrusage(). If your solution on the due date has a score of < 100.00%, you'll receive the extra credit! Good luck!