# Project 2: Separation of concerns: RPCs
**Due: Sept 24, 2023, 11:59:59 pm, v1.01**

### Recent changes
- Clarified that the server should listen to an address with just `:<port>`, no host.

## Big Picture

This project requires you to use gRPC to  split your code from Project
1 into a command-line interface and a *blobserver*. You will also
build a discrete  *lockserver* that responds to gRPC lock and unlock requests. 
the system.

## gRPC

[gRPC](https://grpc.io) ([go
bindings](https://grpc.io/docs/languages/go/quickstart/)) is a powerful,
open-source remote procedure call (RPC) package that works across
heterogeneity of machines and languages. gRPC interfaces are defined
using **protocol buffers**, which are also used to serialize messages.

## Setup
Download the starter files for this project [here](https://sedna.cs.umd.edu/818/projects/p2.tgz?1).

- [Install the protobuf compiler.](https://grpc.io/docs/protoc-installation/)
- [Install the golang bindings.](https://grpc.io/docs/languages/go/quickstart/)
- Update your paths so the `protoc` compiler can find the plugins.

For example, on a mac:
```
   brew install protobuf`
   protoc --version     # at least 24.3
   
   go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28
   go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2

   export PATH="$PATH:$(go env GOPATH)/bin    # for bash
```

### Helloworld

You can download the entire `grpc-go` repo as specified in the
quickstart guide, but it is not necessary for this project. The
supplied files have a version of the `helloworld` example w/ altered
`go.mod` and package statements to make it completely local. You can
go through the quickstart `helloworld` example completely in this
directory. 

The command to rebuild the protocol buffers for the altered directory
would be (from the `helloworld` directory):
```
 protoc --go_out=. --go_opt=paths=source_relative	\
    --go-grpc_out=. --go-grpc_opt=paths=source_relative	\
    pb/helloworld.proto
```

- Learn how gRPC works in [Introduction to gRPC](https://grpc.io/docs/what-is-grpc/introduction/) and [Core concepts](https://grpc.io/docs/what-is-grpc/core-concepts/).
- Work through the [Basics tutorial](https://grpc.io/docs/languages/go/basics/).
- Explore the [API reference](https://grpc.io/docs/languages/go/api).



## Big Picture
There are essentially two parts to this project, both of which that rely on using
gRPCs.

The first part is taking your simple blob store and splitting it to a distinct
blob *store* in `p2/ubi/server.go` and command-line tools `p2/blob.go` and  `p2/lock.go`. 

The second part is building a lock server that will take an *acquire* request for a file
path and acquire read (SHARED) locks on the entire path from the root down to the path's
tail, except that the very last might be a write (EXCLUSIVE) lock, depending on the mode
requested by the gRPC. For example, an incoming acquire (EXCLUSIVE) request for `/foo/bar.go` would
result in a shared lock of `/`, a shared lock on `/foo`, and then an exclusive lock on
`/foo/bar.go`.

Each of these locks should be implemented with `sync.RWMutex`, and acquire requests for
paths already locked with incompatible modes would result in the gRPC request being blocked
until the situation changes.

## Details: gRPC protocol definition

`pb/comm.proto` will define the object services that your servers will respond
to. I gave you the service definition, and the "message" definitions
for the `BlobPutRequest`. Define the others similarly. 

Then, compile the protocol w/ the `protoc` command in the comment.
This will create files `comm.pb.go` and `comm_grpc.pb.go`. The latter
defines the routines that use object *reflection* to generate
communication stubs that call the functions you write in your server. 

The service definition is:
```
service Blob {
    rpc Put (BlobPutRequest) returns (BlobPutReply) {}
    rpc Get (BlobGetRequest) returns (BlobGetReply) {}
    rpc List (BlobListRequest) returns (BlobListReply) {}

    rpc Lock (LockRequest) returns (LockReply) {}
    rpc Unlock (UnlockRequest) returns (UnlockReply) {}
}
```
You will need to define server routines corresponding to each of the
above. For example, the `put` command function will look like:
```
func (s *GRPCserver) Put(ctx context.Context, in *pb.BlobPutRequest) (*pb.BlobPutReply, error) {

       ....
	   
	return &pb.BlobPutReply{Sig: sig, ErrorString: ""}, nil
}
```

Most of this stuff works seamlessly. One oddity is that when you start
up your server (in `server.go`), the actual startup is here:
```
	lis, err := net.Listen("tcp", ServerAddress)
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}
	s := grpc.NewServer()
	pb.RegisterBlobServer(s, &GRPCserver{})

```
The `GRPCserver` is a placeholder structure that does not need to
define any properties or methods, except the `UnimplementedBlobServer`
method. From `ubi/grpcServe.go`
```
type GRPCserver struct {
	pb.UnimplementedBlobServer
}
```

## Details: Locks

Implement the locks using `Sync.RWMutex` from the standard
library. The `path` used to choose which lock to lock/unlock is an
arbitrary string, and *each path must have a distinct lock*. 

This means you need to create `RWMutex`'s at runtime. You can store them
in a map for easy lookup. Though I won't be testing high-speed
concurrent locks, your implementation should work w/ even when
multiple clients are trying to lock a path at the same time, and:
- the path hasn't been accessed before, so no lock is pre-existing
- you must ensure that only one `RWMutex` is create per lock (no
  overwriting!).
  
This is complicated by the fact that gRPC is multi-threaded; each incoming
request is given it's own server go-thread and runs concurrently with
all other requests


## Command-line parameters

The server:
```
   go run server.go [-d] [-s <:port>] 
```

The clients:
```
   go run blob.go [-d] [-s <host:port>] (desc <sig>|get <sig> <destination>|put <source>|list|lock <path>|unlock <path>)
   go run lock.go [-d] [-s <host:port>] (lock <path>|rlock <path>|unlock <path>|runlock <path>)
```

## Testing

The testscript from before is a good place to start, but you also need
locking behavior identical as shown in the video.

## Notes
- Use the `context` to set RPC timeouts to 60 seconds.
- Your server main should spawn off your `http` server in a new goroutine, and be always available.
- gRPC communication requires that the server listen to an address w/o the host, e.g. ":50050", in order to work across the network.

## Grading
I will assign grades vaguely as follow:
- 70 pts - distributing the blobserver
- 30 pts - lockserver


## Video walkthrough

![p2 walkthrough](p2.mp4)

## Submit

Submit by copying into your distro and pushing to gitlab.


