Skip to main content Link Menu Expand (external link) Document Search Copy Copied

KV store

Table of contents

  1. Protocol buffers
  2. Data structures
  3. RPC implementation

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 type bytes) 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 type bytes and return the result of looking up the value in the hash map. If the key is not found, return Err(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 call lock.read() on an RwLock, you can call arc_lock.read() on an Arc<RwLock>. This is because Arc<T> implements Deref, which allows you to treat it like an immutable reference to an object of type T.

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.