jaguard /ˈʤæˌɡɑːd/
- noun: A sentinel from the genus Panthera
- verb: past tense of jaga, to take care
- noun: A Go implementation of ZooKeeper
A Go implementation of Apache Zookeper Protocol for 50.041 Distributed Systems based on the Zookeeper paper and open-source Java implementation of Apache Zookeeper.
- Zookeeper Client (CLI)
- Zookeeper Server Cluster
- Znode implementation
- Znode read/write operations + replication across different servers for maintenance of data tree
- Leader re-election protocol via Zookeeper Atomic Broadcast(ZAB)
- Ephemeral nodes
- File Watch
- Server recovery from previous snapshot
- Linearizable Write
- Wait-free Read: Fast reading from another non-leader node
- Fault tolerance: Consistency despite adversarial conditions
- FIFO client ordering
- Go 1.21
- grpc-go
The *.pb.go
files are currently ignored by git. To generate them, run
$ ./build.sh
Then, run make cli
to start the client and make puppet
to start the Zookeper cluster.
Read about the project structure, design considerations and issues in DESIGN.md.
- how to run
To run the servers for testing, you need to change the directory to /server
and you can run with the following commands.
go run *.go -config=../config.json
It has a few flags for the Checkpoint 2 demonstration.
-multiple_req
: For multiple request through the same server-multiple_cli
: For multiple request through different servers-leader_verbo
: For more detail printing for leader election
The code progress as such
It start from main.go
- Parse the flag
- Initialise each server base on the config.json
- By running
go Run(idx)
- By running
func Run(idx int)
is define in server.go
. The function initalise the server and start the leader election by starting each node go node.Serve(grpc_s)
In func (s *Server) Serve(grpc_s *grpc.Server)
, it will start the leader election and start a new Go routine for the heart beat.
After the method Serve
, the servers are ready for any request. To simulate the request we have another function called func Simulate(s *Server, path string)
which can be found in basic.go
.
To simulate the client request, the function will take in the desired server for the client. Then the function will craft the ZabRequest
to send to the server.
req := &pb.ZabRequest{
Transaction: &pb.Transaction{
Zxid: s.LastZxid.Inc().Raw(),
Path: path,
Data: data,
Type: 1,
Flags: "someFlags",
},
RequestType: pb.RequestType_CLIENT,
}
_, err = c.SendZabRequest(ctx, req)
if err != nil {
log.Printf("%d error sending zab request: %v", s.Id, err)
}
The logic to process all the different types of Zab messages can all be found in the zab.go
.
It has the details for the messages in the leader election and for the function func (s *Server) SendZabRequest(ctx context.Context, in *pb.ZabRequest)
It basically check for two criteria
isLeader := s.GetState() == LEADING
which is a booleanswitch in.RequestType
which can have the follow valuespb.RequestType_PROPOSAL
pb.RequestType_ANNOUNCEMENT
pb.RequestType_CLIENT
In zab.go
, the messages are sent through the SendGrpc
function. The function definition is in messages.go
Here is the function signature:
func SendGrpc[T pb.Message, R pb.Message](
F func(pb.NodeClient, context.Context, T, ...grpc.CallOption) (R, error),
s *Server,
to int,
msg T,
timeout int,
)
This should be sufficient to tell you the general flow of the programme and entry points.
- Ivan Feng
- Joshua Ng
- Sean Yap
- Shi Hui
- Wai Shun