KV store
Table of contents
In this part, you will implement a basic key-value (KV) store by implementing two new RPCs, Put and Get, by following the steps you used to implement the Echo RPC. Keep in mind that the only files you should be editing in this part are proto/kv_store.proto, src/server.rs, and src/client.rs.
Protocol buffers
In proto/kv_store.proto, define two new RPCs: Put and Get.
Putshould take in a key and value (both of typebytes) and store the pair in a hash map. If the key is already in the hash map, the previous value should be overwritten.Getshould take in a key of typebytesand return the result of looking up the value in the hash map. If the key is not found, returnErr(tonic::Status::new(tonic::Code::NotFound, "Key does not exist.")).
Note: The protocol buffer type bytes is translated into Vec<u8> in Rust, which is why the API in src/client.rs has Vec<u8> as the type of keys and values.
Data structures
You will need to add state to your server to store key/value pairs. Since your server is asynchronous and multi-threaded, you will need synchronization to prevent concurrent reads and writes to your data structures.
You should use a tokio::sync::RwLock to synchronize access to your “database” that stores key/value pairs, which can simply be a hash map.
Since this lock must be accessed by several threads, you will need to wrap it with a reference counted pointer. In Rust, we use std::sync::Arc to allow Rust to keep track of references to the lock in a thread-safe manner.
When you are calling methods on an
Arc, you can essentially treat it as a standard pointer. For example, if you can calllock.read()on anRwLock, you can callarc_lock.read()on anArc<RwLock>. This is becauseArc<T>implements Deref, which allows you to treat it like an immutable reference to an object of typeT.
Add a new field to the KvStore struct in src/server.rs to store the reference-counted lock that guards your hash map database. Make sure to initialize it in the start function.
RPC implementation
Implement the server-side stubs for the Put and Get RPCs in src/server.rs, and fill in the corresponding client-side function stubs in src/client.rs. Remember that the bytes type you specified in your protocol buffers translate to the Rust type Vec<u8>, so make sure to pass in a Vec<u8> for fields in Rust structs that correspond to proto fields of type bytes.
Once you are done, you should be able to put key/value pairs into your store by modifying src/client.rs to call your newly defined RPCs. Test your implementation using the instructions from the Usage section.
A working implementation of Echo, Put, and Get should give you a full score on the autograder.