Commit b6eb9e07 authored by Sushant Mahajan's avatar Sushant Mahajan

added documentation for each function and complex blocks of code

parent b4502c1f
...@@ -14,6 +14,7 @@ import ( ...@@ -14,6 +14,7 @@ import (
"time" "time"
) )
/*Constants used throughout the program to identify commands, request, response, and error messages*/
const ( const (
//request //request
SET = "set" SET = "set"
...@@ -36,21 +37,30 @@ const ( ...@@ -36,21 +37,30 @@ const (
ERR_INTERNAL = "ERR_INTERNAL" ERR_INTERNAL = "ERR_INTERNAL"
) )
//represents the value in the main hashtable (key, value) pair
type Data struct { type Data struct {
numBytes uint64 numBytes uint64 //number of bytes of the value bytes
version uint64 version uint64 //current version of the key
expTime uint64 expTime uint64 //time offset in seconds after which the key should expire
value []byte value []byte //bytes representing the actual content of the value
} }
//represents the main hashtable where the dance actually happens
type KeyValueStore struct { type KeyValueStore struct {
dictionary map[string]*Data dictionary map[string]*Data //the hashtable that stores the (key, value) pairs
sync.RWMutex sync.RWMutex //mutex for synchronization when reading or writing to the hashtable
} }
//global version counter
var ver uint64 var ver uint64
//pointer to custom logger
var logger *log.Logger var logger *log.Logger
/*Function to start the server and accept connections.
*arguments: none
*return: none
*/
func startServer() { func startServer() {
logger.Println("Server started") logger.Println("Server started")
listener, err := net.Listen("tcp", ":5000") listener, err := net.Listen("tcp", ":5000")
...@@ -69,10 +79,14 @@ func startServer() { ...@@ -69,10 +79,14 @@ func startServer() {
continue continue
} }
go handleClient(conn, table) go handleClient(conn, table) //client connection handler
} }
} }
/*Function to read data from the connection and put it on the channel so it could be read in a systematic fashion.
*arguments: channel shared between this go routine and other functions performing actions based on the commands given, client connection
*return: none
*/
func myRead(ch chan string, conn net.Conn) { func myRead(ch chan string, conn net.Conn) {
scanner := bufio.NewScanner(conn) scanner := bufio.NewScanner(conn)
for { for {
...@@ -86,6 +100,10 @@ func myRead(ch chan string, conn net.Conn) { ...@@ -86,6 +100,10 @@ func myRead(ch chan string, conn net.Conn) {
} }
} }
/*Simple write function to send information to the client
*arguments: client connection, msg to send to the client
*return: none
*/
func write(conn net.Conn, msg string) { func write(conn net.Conn, msg string) {
buf := []byte(msg) buf := []byte(msg)
buf = append(buf, []byte(CRLF)...) buf = append(buf, []byte(CRLF)...)
...@@ -93,6 +111,10 @@ func write(conn net.Conn, msg string) { ...@@ -93,6 +111,10 @@ func write(conn net.Conn, msg string) {
conn.Write(buf) conn.Write(buf)
} }
/*After initial establishment of the connection with the client, this go routine handles further interaction
*arguments: client connection, pointer to the hastable structure
*return: none
*/
func handleClient(conn net.Conn, table *KeyValueStore) { func handleClient(conn net.Conn, table *KeyValueStore) {
defer conn.Close() defer conn.Close()
//channel for every connection for every client //channel for every connection for every client
...@@ -109,6 +131,10 @@ func handleClient(conn net.Conn, table *KeyValueStore) { ...@@ -109,6 +131,10 @@ func handleClient(conn net.Conn, table *KeyValueStore) {
} }
} }
/*Basic validations for various commands
*arguments: command to check against, other parmameters sent with the command (excluding the value), client connection
*return: integer representing error state
*/
func isValid(cmd string, tokens []string, conn net.Conn) int { func isValid(cmd string, tokens []string, conn net.Conn) int {
var flag int var flag int
switch cmd { switch cmd {
...@@ -133,7 +159,7 @@ func isValid(cmd string, tokens []string, conn net.Conn) int { ...@@ -133,7 +159,7 @@ func isValid(cmd string, tokens []string, conn net.Conn) int {
logger.Println(cmd, ":numBytes invalid") logger.Println(cmd, ":numBytes invalid")
flag = 1 flag = 1
} }
//other validations
case GET: case GET:
if len(tokens) != 2 { if len(tokens) != 2 {
flag = 1 flag = 1
...@@ -143,7 +169,7 @@ func isValid(cmd string, tokens []string, conn net.Conn) int { ...@@ -143,7 +169,7 @@ func isValid(cmd string, tokens []string, conn net.Conn) int {
flag = 1 flag = 1
logger.Println(cmd, ":Invalid key size") logger.Println(cmd, ":Invalid key size")
} }
//other validations
case GETM: case GETM:
if len(tokens) != 2 { if len(tokens) != 2 {
flag = 1 flag = 1
...@@ -152,7 +178,7 @@ func isValid(cmd string, tokens []string, conn net.Conn) int { ...@@ -152,7 +178,7 @@ func isValid(cmd string, tokens []string, conn net.Conn) int {
flag = 1 flag = 1
logger.Println(cmd, ":Invalid key size") logger.Println(cmd, ":Invalid key size")
} }
//other validations
case CAS: case CAS:
if len(tokens) > 6 || len(tokens) < 5 { if len(tokens) > 6 || len(tokens) < 5 {
flag = 1 flag = 1
...@@ -178,7 +204,6 @@ func isValid(cmd string, tokens []string, conn net.Conn) int { ...@@ -178,7 +204,6 @@ func isValid(cmd string, tokens []string, conn net.Conn) int {
flag = 1 flag = 1
} }
//other validations
case DELETE: case DELETE:
if len(tokens) != 2 { if len(tokens) != 2 {
flag = 1 flag = 1
...@@ -187,7 +212,7 @@ func isValid(cmd string, tokens []string, conn net.Conn) int { ...@@ -187,7 +212,7 @@ func isValid(cmd string, tokens []string, conn net.Conn) int {
flag = 1 flag = 1
logger.Println(cmd, ":Invalid size of key") logger.Println(cmd, ":Invalid size of key")
} }
//other validations
default: default:
return 0 return 0
} }
...@@ -200,15 +225,20 @@ func isValid(cmd string, tokens []string, conn net.Conn) int { ...@@ -200,15 +225,20 @@ func isValid(cmd string, tokens []string, conn net.Conn) int {
return flag return flag
} }
/*Function parses the command provided by the client and delegates further action to command specific functions.
*Based on the return values of those functions, send appropriate messages to the client.
*arguments: client connection, message from client, pointer to hashtable structure, channel shared with myRead function
*return: none
*/
func parseInput(conn net.Conn, msg string, table *KeyValueStore, ch chan string) { func parseInput(conn net.Conn, msg string, table *KeyValueStore, ch chan string) {
tokens := strings.Fields(msg) tokens := strings.Fields(msg)
//general error, don't check for commands, avoid the pain //general error, don't check for commands, avoid the pain ;)
if len(tokens) > 6 { if len(tokens) > 6 {
write(conn, ERR_CMD_ERR) write(conn, ERR_CMD_ERR)
return return
} }
var buffer bytes.Buffer var buffer bytes.Buffer //for efficient string concatenation
//logger.Println(tokens) //logger.Println(tokens)
switch tokens[0] { switch tokens[0] {
case SET: case SET:
...@@ -366,6 +396,10 @@ func readValue(ch chan string, n uint64) ([]byte, bool) { ...@@ -366,6 +396,10 @@ func readValue(ch chan string, n uint64) ([]byte, bool) {
return []byte(v), err return []byte(v), err
} }
/*Delegate function responsible for all parsing and hashtable interactions for the SET command sent by client
*arguments: client connection, tokenized command sent by the client, pointer to hashtable structure, channel shared with myRead
*return: version of inserted key (if successful, 0 otherwise), success or failure, whether to send reply to client
*/
func performSet(conn net.Conn, tokens []string, table *KeyValueStore, ch chan string) (uint64, bool, bool) { func performSet(conn net.Conn, tokens []string, table *KeyValueStore, ch chan string) (uint64, bool, bool) {
k := tokens[0] k := tokens[0]
e, _ := strconv.ParseUint(tokens[1], 10, 64) //expiry time offset e, _ := strconv.ParseUint(tokens[1], 10, 64) //expiry time offset
...@@ -405,6 +439,10 @@ func performSet(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st ...@@ -405,6 +439,10 @@ func performSet(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st
} }
} }
/*Delegate function reponsible for activities related to the GET command sent by the client.
*arguments: client connection, tokenized command sent by the client, pointer to hashtable structure
*return: pointer to value corresponding to the key given by client, success or failure
*/
func performGet(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, bool) { func performGet(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, bool) {
k := tokens[0] k := tokens[0]
defer table.RUnlock() defer table.RUnlock()
...@@ -424,6 +462,10 @@ func performGet(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, bo ...@@ -424,6 +462,10 @@ func performGet(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, bo
} }
} }
/*Delegate function reponsible for activities related to the GETM command sent by the client.
*arguments: client connection, tokenized command sent by the client, pointer to hashtable structure
*return: pointer to value corresponding to the key given by client, success or failure
*/
func performGetm(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, bool) { func performGetm(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, bool) {
k := tokens[0] k := tokens[0]
defer table.RUnlock() defer table.RUnlock()
...@@ -445,6 +487,11 @@ func performGetm(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, b ...@@ -445,6 +487,11 @@ func performGetm(conn net.Conn, tokens []string, table *KeyValueStore) (*Data, b
} }
} }
/*Delegate function reponsible for activities related to the CAS command sent by the client.
*arguments: client connection, tokenized command sent by the client, pointer to hashtable structure, channel shared with myRead
*return: new version of updated key (if it is updated), error status {0: error while reading new value, 1: key found and changed,
*2: version mismatch with key, 3: key not found}, whether to reply to client
*/
func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan string) (uint64, int, bool) { func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan string) (uint64, int, bool) {
k := tokens[0] k := tokens[0]
e, _ := strconv.ParseUint(tokens[1], 10, 64) e, _ := strconv.ParseUint(tokens[1], 10, 64)
...@@ -465,7 +512,7 @@ func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st ...@@ -465,7 +512,7 @@ func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st
table.Lock() table.Lock()
if val, ok := table.dictionary[k]; ok { if val, ok := table.dictionary[k]; ok {
if val.version == ve { if val.version == ve {
if e == 0 { if e == 0 { //if expiry time is zero, key should not be deleted
val.expTime = e val.expTime = e
} else { } else {
val.expTime = e + uint64(time.Now().Unix()) val.expTime = e + uint64(time.Now().Unix())
...@@ -482,6 +529,10 @@ func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st ...@@ -482,6 +529,10 @@ func performCas(conn net.Conn, tokens []string, table *KeyValueStore, ch chan st
} }
} }
/*Delegate function reponsible for activities related to the DELETE command sent by the client.
*arguments: client connection, tokenized command sent by the client, pointer to hashtable structure
*return: integer secifying error state {0: found and deleted, 1: found but expired (deleted but client told non-existent, 2: key not found}
*/
func performDelete(conn net.Conn, tokens []string, table *KeyValueStore) int { func performDelete(conn net.Conn, tokens []string, table *KeyValueStore) int {
k := tokens[0] k := tokens[0]
logger.Println(tokens) logger.Println(tokens)
...@@ -501,6 +552,10 @@ func performDelete(conn net.Conn, tokens []string, table *KeyValueStore) int { ...@@ -501,6 +552,10 @@ func performDelete(conn net.Conn, tokens []string, table *KeyValueStore) int {
return 2 //key not found return 2 //key not found
} }
/*Simple function that dumps the contents of the hashtable
*arguments: pointer to the hashtable structure
*return: none
*/
func debug(table *KeyValueStore) { func debug(table *KeyValueStore) {
logger.Println("----start debug----") logger.Println("----start debug----")
for key, val := range (*table).dictionary { for key, val := range (*table).dictionary {
...@@ -509,6 +564,10 @@ func debug(table *KeyValueStore) { ...@@ -509,6 +564,10 @@ func debug(table *KeyValueStore) {
logger.Println("----end debug----") logger.Println("----end debug----")
} }
/*Entry point of this program. Initializes the start of ther server and sets up the logger.
*arguments: none
*return: none
*/
func main() { func main() {
ver = 1 ver = 1
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment