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
.
Put
should 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.Get
should take in a key of typebytes
and 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.