github.com/DARA-Project/GoDist-Scheduler@v0.0.0-20201030134746-668de4acea0d/structured/rsm/kvservicemain.go (about)

     1  // A simple key-value store that supports four API calls over RPC:
     2  // val <- get(key,clientid,clock): execute k-v get from clientid with lamport clock value
     3  // - put(key,val,clientid,clock) : execute k-v put from clientid with lamport clock value
     4  // - clockupdate(clientid,clock) : report latest lamport clock value at clientid
     5  // - disconnect(clientid)        : report disconnection/failure of clientid
     6  //
     7  // Usage: go run kvservicemain.go [ip:port] [num-clients]
     8  //
     9  // - [ip:port] : the ip and TCP port on which the service will listen
    10  //               for connections
    11  //
    12  // - [num-clients] : Number of unique clients to expect (unique clientid's)
    13  
    14  package main
    15  
    16  import (
    17  	"fmt"
    18  	"log"
    19  //	"math/rand"
    20  	"net"
    21  	"net/rpc"
    22  	"os"
    23  	"strconv"
    24  	"sync"
    25  	"time"
    26  )
    27  
    28  // args in get(args)
    29  type GetArgs struct {
    30  	Key      string // key to look up
    31  	Clientid uint8  // client id issuing this get
    32  	Clock    uint64 // value of lamport clock at the issuing client
    33  }
    34  
    35  // args in put(args)
    36  type PutArgs struct {
    37  	Key      string // key to associate value with
    38  	Val      string // value
    39  	Clientid uint8  // client id issueing this put
    40  	Clock    uint64 // value of lamport clock at the issuing client
    41  }
    42  
    43  // args in clockupdate(args)
    44  type ClockUpdateArgs struct {
    45  	Clientid uint8  // client id issueing this put
    46  	Clock    uint64 // value of lamport clock at the issuing client
    47  }
    48  
    49  // args in disconnect(args)
    50  type DisconnectArgs struct {
    51  	Clientid uint8 // client id issueing this put
    52  }
    53  
    54  // Reply from service for all the API calls above.
    55  type ValReply struct {
    56  	Val string // value; depends on the call
    57  }
    58  
    59  // Value in the key-val store.
    60  type MapVal struct {
    61  	value string // the underlying value representation
    62  }
    63  
    64  type ClientInfo struct {
    65  	Clock uint64
    66  	Alive bool
    67  	LastReq uint64
    68  }
    69  
    70  // Map implementing the key-value store: use this for k-v storage.
    71  var kvmap map[string]*MapVal
    72  var kvMux sync.Mutex
    73  
    74  var clientinfomap map[uint8]*ClientInfo
    75  var cliMux sync.Mutex
    76  
    77  type KeyValService int
    78  
    79  // Command line arg.
    80  var numClients uint8
    81  
    82  func updateAddClientClock(clientID uint8, clock uint64) {
    83  	if client, ok := clientinfomap[clientID]; ok {
    84  		client.Clock = clock
    85  		clientinfomap[clientID] = client
    86  	} else {
    87  		client := &ClientInfo{clock, true, 0}
    88  		clientinfomap[clientID] = client
    89  	}
    90  }
    91  
    92  func addClientRequestInfo(clientID uint8, request uint64) {
    93  	if client, ok := clientinfomap[clientID]; ok {
    94  		client.LastReq = request
    95  		clientinfomap[clientID] = client
    96  	}
    97  }
    98  
    99  func deleteClientRequestInfo(clientID uint8) {
   100  	if client, ok := clientinfomap[clientID]; ok {
   101  		client.LastReq = 0
   102  		clientinfomap[clientID] = client
   103  	}
   104  }
   105  
   106  func check_stability(clientID uint8, clock uint64) bool {
   107  	var clients_checked uint8
   108  	clients_checked = 0
   109  	for cid, client := range clientinfomap {
   110  		// Don't check for dead clients for stability
   111  		if client.Alive == false {
   112  			clients_checked += 1
   113  			continue
   114  		}
   115  		if clientID == cid {
   116  			clients_checked += 1
   117  			continue
   118  		}
   119  		// Check if the client has a lower or equal clock so we must return as this request is not stable yet.
   120  		if client.Clock <= clock {
   121  			return false
   122  		}
   123  		// Check if any client has an earlier pending request which should be served first
   124  		if client.LastReq != 0 && client.LastReq < clock {
   125  			return false
   126  		}
   127  		// Tie-breaker rule
   128  		if client.LastReq == clock && cid < clientID {
   129  			return false
   130  		}
   131  		clients_checked += 1
   132  	}
   133  	// Message is not stable if we have not received a single other message from any other client.
   134  	if clients_checked != numClients { 
   135  		return false
   136  	}
   137  	return true
   138  }
   139  
   140  func returnWhenStable(clientID uint8, clock uint64) {
   141  	is_stable := false
   142  	for {
   143  		if is_stable {
   144  			break
   145  		}
   146  		ticker := time.NewTicker(100 * time.Millisecond)
   147  		select {
   148  			case <- ticker.C:
   149  				cliMux.Lock()
   150  				is_stable = check_stability(clientID, clock)
   151  				cliMux.Unlock()
   152  		}
   153  	}
   154  }
   155  
   156  // GET
   157  func (kvs *KeyValService) Get(args *GetArgs, reply *ValReply) error {
   158  	clientID := args.Clientid
   159  	clock := args.Clock
   160  	log.Printf("[GET-R] Client %d Clock %d\n", clientID, clock)
   161  	cliMux.Lock()
   162  	updateAddClientClock(clientID, clock)
   163  	addClientRequestInfo(clientID, clock)
   164  	cliMux.Unlock()
   165  	returnWhenStable(clientID, clock)
   166  	kvMux.Lock()
   167  	key := args.Key
   168  	if v, ok := kvmap[key]; ok {
   169  		reply.Val = v.value
   170  	} else {
   171  		reply.Val = ""
   172  	}
   173  	kvMux.Unlock()
   174  	cliMux.Lock()
   175  	deleteClientRequestInfo(clientID)
   176  	cliMux.Unlock()
   177  	log.Printf("[GET-S] Client %d Clock %d\n", clientID, clock)
   178  	return nil
   179  }
   180  
   181  // PUT
   182  func (kvs *KeyValService) Put(args *PutArgs, reply *ValReply) error {
   183  	clientID := args.Clientid
   184  	clock := args.Clock
   185  	log.Printf("[PUT-R] Client %d Clock %d\n", clientID, clock)
   186  	cliMux.Lock()
   187  	updateAddClientClock(clientID, clock)
   188  	addClientRequestInfo(clientID, clock)
   189  	cliMux.Unlock()
   190  	returnWhenStable(clientID, clock)
   191  	kvMux.Lock()
   192  	key := args.Key
   193  	val := args.Val
   194  	mapVal := &MapVal{val}
   195  	kvmap[key] = mapVal
   196  	reply.Val = ""
   197  	kvMux.Unlock()
   198  	cliMux.Lock()
   199  	deleteClientRequestInfo(clientID)
   200  	cliMux.Unlock()
   201  	log.Printf("[PUT-S] Client %d Clock %d\n", clientID, clock)
   202  	return nil
   203  }
   204  
   205  // CLOCKUPDATE
   206  func (kvs *KeyValService) ClockUpdate(args *ClockUpdateArgs, reply *ValReply) error {
   207  	clientID := args.Clientid
   208  	clock := args.Clock
   209  	//log.Printf("[UPD-R] Client %d Clock %d\n", clientID, clock)
   210  	cliMux.Lock()
   211  	updateAddClientClock(clientID, clock)
   212  	cliMux.Unlock()
   213  	//log.Printf("[UPD-S] Client %d Clock %d\n", clientID, clock)
   214  	return nil
   215  }
   216  
   217  // DISCONNECT
   218  func (kvs *KeyValService) Disconnect(args *DisconnectArgs, reply *ValReply) error {
   219  	clientID := args.Clientid
   220  	log.Printf("[DSN-R] Client %d\n", clientID)
   221  	cliMux.Lock()
   222  	if client, ok := clientinfomap[clientID]; ok {
   223  		client.Alive = false
   224  		clientinfomap[clientID] = client
   225  	}
   226  	reply.Val = ""
   227  	cliMux.Unlock()
   228  	log.Printf("[DSN-S] Client %d\n", clientID)
   229  	return nil
   230  }
   231  
   232  // Main server loop.
   233  func main() {
   234  	// Parse args.
   235  	usage := fmt.Sprintf("Usage: %s [ip:port] [num-clients]\n", os.Args[0])
   236  	if len(os.Args) != 3 {
   237  		fmt.Printf(usage)
   238  		os.Exit(1)
   239  	}
   240  
   241  	ip_port := os.Args[1]
   242  	arg, err := strconv.ParseUint(os.Args[2], 10, 8)
   243  	if err != nil {
   244  		fmt.Println(err)
   245  		os.Exit(1)
   246  	}
   247  	if arg == 0 {
   248  		fmt.Printf(usage)
   249  		fmt.Printf("\tnum-clients arg must be non-zero\n")
   250  		os.Exit(1)
   251  	}
   252  	numClients = uint8(arg)
   253  
   254  	// Setup key-value store and register service.
   255  	kvservice := new(KeyValService)
   256  	rpc.Register(kvservice)
   257  	l, e := net.Listen("tcp", ip_port)
   258  	if e != nil {
   259  		log.Fatal("listen error:", e)
   260  	}
   261  
   262  	// Init maps
   263  	kvmap = map[string]*MapVal{}
   264  	clientinfomap = map[uint8]*ClientInfo{}
   265  
   266  	// TODO: Enter servicing loop, like:
   267  
   268  	// for {
   269  	// conn, _ := l.Accept()
   270  	// go rpc.ServeConn(conn)
   271  	// }
   272  	log.Println("Business is open")
   273  	for {
   274  		conn, _ := l.Accept()
   275  		go rpc.ServeConn(conn)
   276  	}
   277  }
   278