github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/libnetwork/cmd/networkdb-test/dbclient/ndbClient.go (about)

     1  package dbclient
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"net"
     8  	"net/http"
     9  	"os"
    10  	"regexp"
    11  	"strconv"
    12  	"strings"
    13  	"time"
    14  
    15  	"github.com/containerd/log"
    16  )
    17  
    18  var servicePort string
    19  
    20  const totalWrittenKeys string = "totalKeys"
    21  
    22  type resultTuple struct {
    23  	id     string
    24  	result int
    25  }
    26  
    27  func httpGetFatalError(ip, port, path string) {
    28  	body, err := httpGet(ip, port, path)
    29  	if err != nil || !strings.Contains(string(body), "OK") {
    30  		log.G(context.TODO()).Fatalf("[%s] error %s %s", path, err, body)
    31  	}
    32  }
    33  
    34  func httpGet(ip, port, path string) ([]byte, error) {
    35  	resp, err := http.Get("http://" + ip + ":" + port + path)
    36  	if err != nil {
    37  		log.G(context.TODO()).Errorf("httpGet error:%s", err)
    38  		return nil, err
    39  	}
    40  	defer resp.Body.Close()
    41  	body, err := io.ReadAll(resp.Body)
    42  	return body, err
    43  }
    44  
    45  func joinCluster(ip, port string, members []string, doneCh chan resultTuple) {
    46  	httpGetFatalError(ip, port, "/join?members="+strings.Join(members, ","))
    47  
    48  	if doneCh != nil {
    49  		doneCh <- resultTuple{id: ip, result: 0}
    50  	}
    51  }
    52  
    53  func joinNetwork(ip, port, network string, doneCh chan resultTuple) {
    54  	httpGetFatalError(ip, port, "/joinnetwork?nid="+network)
    55  
    56  	if doneCh != nil {
    57  		doneCh <- resultTuple{id: ip, result: 0}
    58  	}
    59  }
    60  
    61  func leaveNetwork(ip, port, network string, doneCh chan resultTuple) {
    62  	httpGetFatalError(ip, port, "/leavenetwork?nid="+network)
    63  
    64  	if doneCh != nil {
    65  		doneCh <- resultTuple{id: ip, result: 0}
    66  	}
    67  }
    68  
    69  func writeTableKey(ip, port, networkName, tableName, key string) {
    70  	createPath := "/createentry?unsafe&nid=" + networkName + "&tname=" + tableName + "&value=v&key="
    71  	httpGetFatalError(ip, port, createPath+key)
    72  }
    73  
    74  func deleteTableKey(ip, port, networkName, tableName, key string) {
    75  	deletePath := "/deleteentry?nid=" + networkName + "&tname=" + tableName + "&key="
    76  	httpGetFatalError(ip, port, deletePath+key)
    77  }
    78  
    79  func clusterPeersNumber(ip, port string, doneCh chan resultTuple) {
    80  	body, err := httpGet(ip, port, "/clusterpeers")
    81  	if err != nil {
    82  		log.G(context.TODO()).Errorf("clusterPeers %s there was an error: %s", ip, err)
    83  		doneCh <- resultTuple{id: ip, result: -1}
    84  		return
    85  	}
    86  	peersRegexp := regexp.MustCompile(`total entries: ([0-9]+)`)
    87  	peersNum, _ := strconv.Atoi(peersRegexp.FindStringSubmatch(string(body))[1])
    88  
    89  	doneCh <- resultTuple{id: ip, result: peersNum}
    90  }
    91  
    92  func networkPeersNumber(ip, port, networkName string, doneCh chan resultTuple) {
    93  	body, err := httpGet(ip, port, "/networkpeers?nid="+networkName)
    94  	if err != nil {
    95  		log.G(context.TODO()).Errorf("networkPeersNumber %s there was an error: %s", ip, err)
    96  		doneCh <- resultTuple{id: ip, result: -1}
    97  		return
    98  	}
    99  	peersRegexp := regexp.MustCompile(`total entries: ([0-9]+)`)
   100  	peersNum, _ := strconv.Atoi(peersRegexp.FindStringSubmatch(string(body))[1])
   101  
   102  	doneCh <- resultTuple{id: ip, result: peersNum}
   103  }
   104  
   105  func dbTableEntriesNumber(ip, port, networkName, tableName string, doneCh chan resultTuple) {
   106  	body, err := httpGet(ip, port, "/gettable?nid="+networkName+"&tname="+tableName)
   107  	if err != nil {
   108  		log.G(context.TODO()).Errorf("tableEntriesNumber %s there was an error: %s", ip, err)
   109  		doneCh <- resultTuple{id: ip, result: -1}
   110  		return
   111  	}
   112  	elementsRegexp := regexp.MustCompile(`total entries: ([0-9]+)`)
   113  	entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1])
   114  	doneCh <- resultTuple{id: ip, result: entriesNum}
   115  }
   116  
   117  func dbQueueLength(ip, port, networkName string, doneCh chan resultTuple) {
   118  	body, err := httpGet(ip, port, "/networkstats?nid="+networkName)
   119  	if err != nil {
   120  		log.G(context.TODO()).Errorf("queueLength %s there was an error: %s", ip, err)
   121  		doneCh <- resultTuple{id: ip, result: -1}
   122  		return
   123  	}
   124  	elementsRegexp := regexp.MustCompile(`qlen: ([0-9]+)`)
   125  	entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1])
   126  	doneCh <- resultTuple{id: ip, result: entriesNum}
   127  }
   128  
   129  func clientWatchTable(ip, port, networkName, tableName string, doneCh chan resultTuple) {
   130  	httpGetFatalError(ip, port, "/watchtable?nid="+networkName+"&tname="+tableName)
   131  	if doneCh != nil {
   132  		doneCh <- resultTuple{id: ip, result: 0}
   133  	}
   134  }
   135  
   136  func clientTableEntriesNumber(ip, port, networkName, tableName string, doneCh chan resultTuple) {
   137  	body, err := httpGet(ip, port, "/watchedtableentries?nid="+networkName+"&tname="+tableName)
   138  	if err != nil {
   139  		log.G(context.TODO()).Errorf("clientTableEntriesNumber %s there was an error: %s", ip, err)
   140  		doneCh <- resultTuple{id: ip, result: -1}
   141  		return
   142  	}
   143  	elementsRegexp := regexp.MustCompile(`total elements: ([0-9]+)`)
   144  	entriesNum, _ := strconv.Atoi(elementsRegexp.FindStringSubmatch(string(body))[1])
   145  	doneCh <- resultTuple{id: ip, result: entriesNum}
   146  }
   147  
   148  func writeKeysNumber(ip, port, networkName, tableName, key string, number int, doneCh chan resultTuple) {
   149  	x := 0
   150  	for ; x < number; x++ {
   151  		k := key + strconv.Itoa(x)
   152  		// write key
   153  		writeTableKey(ip, port, networkName, tableName, k)
   154  	}
   155  	doneCh <- resultTuple{id: ip, result: x}
   156  }
   157  
   158  func deleteKeysNumber(ip, port, networkName, tableName, key string, number int, doneCh chan resultTuple) {
   159  	x := 0
   160  	for ; x < number; x++ {
   161  		k := key + strconv.Itoa(x)
   162  		// write key
   163  		deleteTableKey(ip, port, networkName, tableName, k)
   164  	}
   165  	doneCh <- resultTuple{id: ip, result: x}
   166  }
   167  
   168  func writeUniqueKeys(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) {
   169  	for x := 0; ; x++ {
   170  		select {
   171  		case <-ctx.Done():
   172  			doneCh <- resultTuple{id: ip, result: x}
   173  			return
   174  		default:
   175  			k := key + strconv.Itoa(x)
   176  			// write key
   177  			writeTableKey(ip, port, networkName, tableName, k)
   178  			// give time to send out key writes
   179  			time.Sleep(100 * time.Millisecond)
   180  		}
   181  	}
   182  }
   183  
   184  func writeDeleteUniqueKeys(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) {
   185  	for x := 0; ; x++ {
   186  		select {
   187  		case <-ctx.Done():
   188  			doneCh <- resultTuple{id: ip, result: x}
   189  			return
   190  		default:
   191  			k := key + strconv.Itoa(x)
   192  			// write key
   193  			writeTableKey(ip, port, networkName, tableName, k)
   194  			// give time to send out key writes
   195  			time.Sleep(100 * time.Millisecond)
   196  			// delete key
   197  			deleteTableKey(ip, port, networkName, tableName, k)
   198  		}
   199  	}
   200  }
   201  
   202  func writeDeleteLeaveJoin(ctx context.Context, ip, port, networkName, tableName, key string, doneCh chan resultTuple) {
   203  	for x := 0; ; x++ {
   204  		select {
   205  		case <-ctx.Done():
   206  			doneCh <- resultTuple{id: ip, result: x}
   207  			return
   208  		default:
   209  			k := key + strconv.Itoa(x)
   210  			// write key
   211  			writeTableKey(ip, port, networkName, tableName, k)
   212  			time.Sleep(100 * time.Millisecond)
   213  			// delete key
   214  			deleteTableKey(ip, port, networkName, tableName, k)
   215  			// give some time
   216  			time.Sleep(100 * time.Millisecond)
   217  			// leave network
   218  			leaveNetwork(ip, port, networkName, nil)
   219  			// join network
   220  			joinNetwork(ip, port, networkName, nil)
   221  		}
   222  	}
   223  }
   224  
   225  func ready(ip, port string, doneCh chan resultTuple) {
   226  	for {
   227  		body, err := httpGet(ip, port, "/ready")
   228  		if err != nil || !strings.Contains(string(body), "OK") {
   229  			time.Sleep(500 * time.Millisecond)
   230  			continue
   231  		}
   232  		// success
   233  		break
   234  	}
   235  	// notify the completion
   236  	doneCh <- resultTuple{id: ip, result: 0}
   237  }
   238  
   239  func checkTable(ctx context.Context, ips []string, port, networkName, tableName string, expectedEntries int, fn func(string, string, string, string, chan resultTuple)) (opTime time.Duration) {
   240  	startTime := time.Now().UnixNano()
   241  	var successTime int64
   242  
   243  	// Loop for 2 minutes to guarantee that the result is stable
   244  	for {
   245  		select {
   246  		case <-ctx.Done():
   247  			// Validate test success, if the time is set means that all the tables are empty
   248  			if successTime != 0 {
   249  				opTime = time.Duration(successTime-startTime) / time.Millisecond
   250  				log.G(ctx).Infof("Check table passed, the cluster converged in %d msec", opTime)
   251  				return
   252  			}
   253  			log.G(ctx).Fatal("Test failed, there is still entries in the tables of the nodes")
   254  		default:
   255  			log.G(ctx).Infof("Checking table %s expected %d", tableName, expectedEntries)
   256  			doneCh := make(chan resultTuple, len(ips))
   257  			for _, ip := range ips {
   258  				go fn(ip, servicePort, networkName, tableName, doneCh)
   259  			}
   260  
   261  			nodesWithCorrectEntriesNum := 0
   262  			for i := len(ips); i > 0; i-- {
   263  				tableEntries := <-doneCh
   264  				log.G(ctx).Infof("Node %s has %d entries", tableEntries.id, tableEntries.result)
   265  				if tableEntries.result == expectedEntries {
   266  					nodesWithCorrectEntriesNum++
   267  				}
   268  			}
   269  			close(doneCh)
   270  			if nodesWithCorrectEntriesNum == len(ips) {
   271  				if successTime == 0 {
   272  					successTime = time.Now().UnixNano()
   273  					log.G(ctx).Infof("Success after %d msec", time.Duration(successTime-startTime)/time.Millisecond)
   274  				}
   275  			} else {
   276  				successTime = 0
   277  			}
   278  			time.Sleep(10 * time.Second)
   279  		}
   280  	}
   281  }
   282  
   283  func waitWriters(parallelWriters int, mustWrite bool, doneCh chan resultTuple) map[string]int {
   284  	var totalKeys int
   285  	resultTable := make(map[string]int)
   286  	for i := 0; i < parallelWriters; i++ {
   287  		log.G(context.TODO()).Infof("Waiting for %d workers", parallelWriters-i)
   288  		workerReturn := <-doneCh
   289  		totalKeys += workerReturn.result
   290  		if mustWrite && workerReturn.result == 0 {
   291  			log.G(context.TODO()).Fatalf("The worker %s did not write any key %d == 0", workerReturn.id, workerReturn.result)
   292  		}
   293  		if !mustWrite && workerReturn.result != 0 {
   294  			log.G(context.TODO()).Fatalf("The worker %s was supposed to return 0 instead %d != 0", workerReturn.id, workerReturn.result)
   295  		}
   296  		if mustWrite {
   297  			resultTable[workerReturn.id] = workerReturn.result
   298  			log.G(context.TODO()).Infof("The worker %s wrote %d keys", workerReturn.id, workerReturn.result)
   299  		}
   300  	}
   301  	resultTable[totalWrittenKeys] = totalKeys
   302  	return resultTable
   303  }
   304  
   305  // ready
   306  func doReady(ips []string) {
   307  	doneCh := make(chan resultTuple, len(ips))
   308  	// check all the nodes
   309  	for _, ip := range ips {
   310  		go ready(ip, servicePort, doneCh)
   311  	}
   312  	// wait for the readiness of all nodes
   313  	for i := len(ips); i > 0; i-- {
   314  		<-doneCh
   315  	}
   316  	close(doneCh)
   317  }
   318  
   319  // join
   320  func doJoin(ips []string) {
   321  	doneCh := make(chan resultTuple, len(ips))
   322  	// check all the nodes
   323  	for i, ip := range ips {
   324  		members := append([]string(nil), ips[:i]...)
   325  		members = append(members, ips[i+1:]...)
   326  		go joinCluster(ip, servicePort, members, doneCh)
   327  	}
   328  	// wait for the readiness of all nodes
   329  	for i := len(ips); i > 0; i-- {
   330  		<-doneCh
   331  	}
   332  	close(doneCh)
   333  }
   334  
   335  // cluster-peers expectedNumberPeers maxRetry
   336  func doClusterPeers(ips []string, args []string) {
   337  	doneCh := make(chan resultTuple, len(ips))
   338  	expectedPeers, _ := strconv.Atoi(args[0])
   339  	maxRetry, _ := strconv.Atoi(args[1])
   340  	for retry := 0; retry < maxRetry; retry++ {
   341  		// check all the nodes
   342  		for _, ip := range ips {
   343  			go clusterPeersNumber(ip, servicePort, doneCh)
   344  		}
   345  		var failed bool
   346  		// wait for the readiness of all nodes
   347  		for i := len(ips); i > 0; i-- {
   348  			node := <-doneCh
   349  			if node.result != expectedPeers {
   350  				failed = true
   351  				if retry == maxRetry-1 {
   352  					log.G(context.TODO()).Fatalf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result)
   353  				} else {
   354  					log.G(context.TODO()).Warnf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result)
   355  				}
   356  				time.Sleep(1 * time.Second)
   357  			}
   358  		}
   359  		// check if needs retry
   360  		if !failed {
   361  			break
   362  		}
   363  	}
   364  	close(doneCh)
   365  }
   366  
   367  // join-network networkName
   368  func doJoinNetwork(ips []string, args []string) {
   369  	doneCh := make(chan resultTuple, len(ips))
   370  	// check all the nodes
   371  	for _, ip := range ips {
   372  		go joinNetwork(ip, servicePort, args[0], doneCh)
   373  	}
   374  	// wait for the readiness of all nodes
   375  	for i := len(ips); i > 0; i-- {
   376  		<-doneCh
   377  	}
   378  	close(doneCh)
   379  }
   380  
   381  // leave-network networkName
   382  func doLeaveNetwork(ips []string, args []string) {
   383  	doneCh := make(chan resultTuple, len(ips))
   384  	// check all the nodes
   385  	for _, ip := range ips {
   386  		go leaveNetwork(ip, servicePort, args[0], doneCh)
   387  	}
   388  	// wait for the readiness of all nodes
   389  	for i := len(ips); i > 0; i-- {
   390  		<-doneCh
   391  	}
   392  	close(doneCh)
   393  }
   394  
   395  // network-peers networkName expectedNumberPeers maxRetry
   396  func doNetworkPeers(ips []string, args []string) {
   397  	doneCh := make(chan resultTuple, len(ips))
   398  	networkName := args[0]
   399  	expectedPeers, _ := strconv.Atoi(args[1])
   400  	maxRetry, _ := strconv.Atoi(args[2])
   401  	for retry := 0; retry < maxRetry; retry++ {
   402  		// check all the nodes
   403  		for _, ip := range ips {
   404  			go networkPeersNumber(ip, servicePort, networkName, doneCh)
   405  		}
   406  		var failed bool
   407  		// wait for the readiness of all nodes
   408  		for i := len(ips); i > 0; i-- {
   409  			node := <-doneCh
   410  			if node.result != expectedPeers {
   411  				failed = true
   412  				if retry == maxRetry-1 {
   413  					log.G(context.TODO()).Fatalf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result)
   414  				} else {
   415  					log.G(context.TODO()).Warnf("Expected peers from %s mismatch %d != %d", node.id, expectedPeers, node.result)
   416  				}
   417  				time.Sleep(1 * time.Second)
   418  			}
   419  		}
   420  		// check if needs retry
   421  		if !failed {
   422  			break
   423  		}
   424  	}
   425  	close(doneCh)
   426  }
   427  
   428  // network-stats-queue networkName <gt/lt> queueSize
   429  func doNetworkStatsQueue(ips []string, args []string) {
   430  	doneCh := make(chan resultTuple, len(ips))
   431  	networkName := args[0]
   432  	comparison := args[1]
   433  	size, _ := strconv.Atoi(args[2])
   434  
   435  	// check all the nodes
   436  	for _, ip := range ips {
   437  		go dbQueueLength(ip, servicePort, networkName, doneCh)
   438  	}
   439  
   440  	var avgQueueSize int
   441  	// wait for the readiness of all nodes
   442  	for i := len(ips); i > 0; i-- {
   443  		node := <-doneCh
   444  		switch comparison {
   445  		case "lt":
   446  			if node.result > size {
   447  				log.G(context.TODO()).Fatalf("Expected queue size from %s to be %d < %d", node.id, node.result, size)
   448  			}
   449  		case "gt":
   450  			if node.result < size {
   451  				log.G(context.TODO()).Fatalf("Expected queue size from %s to be %d > %d", node.id, node.result, size)
   452  			}
   453  		default:
   454  			log.G(context.TODO()).Fatal("unknown comparison operator")
   455  		}
   456  		avgQueueSize += node.result
   457  	}
   458  	close(doneCh)
   459  	avgQueueSize /= len(ips)
   460  	fmt.Fprintf(os.Stderr, "doNetworkStatsQueue succeeded with avg queue:%d", avgQueueSize)
   461  }
   462  
   463  // write-keys networkName tableName parallelWriters numberOfKeysEach
   464  func doWriteKeys(ips []string, args []string) {
   465  	networkName := args[0]
   466  	tableName := args[1]
   467  	parallelWriters, _ := strconv.Atoi(args[2])
   468  	numberOfKeys, _ := strconv.Atoi(args[3])
   469  
   470  	doneCh := make(chan resultTuple, parallelWriters)
   471  	// Enable watch of tables from clients
   472  	for i := 0; i < parallelWriters; i++ {
   473  		go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh)
   474  	}
   475  	waitWriters(parallelWriters, false, doneCh)
   476  
   477  	// Start parallel writers that will create and delete unique keys
   478  	defer close(doneCh)
   479  	for i := 0; i < parallelWriters; i++ {
   480  		key := "key-" + strconv.Itoa(i) + "-"
   481  		log.G(context.TODO()).Infof("Spawn worker: %d on IP:%s", i, ips[i])
   482  		go writeKeysNumber(ips[i], servicePort, networkName, tableName, key, numberOfKeys, doneCh)
   483  	}
   484  
   485  	// Sync with all the writers
   486  	keyMap := waitWriters(parallelWriters, true, doneCh)
   487  	log.G(context.TODO()).Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
   488  
   489  	// check table entries for 2 minutes
   490  	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
   491  	opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keyMap[totalWrittenKeys], dbTableEntriesNumber)
   492  	cancel()
   493  	fmt.Fprintf(os.Stderr, "doWriteKeys succeeded in %d msec", opTime)
   494  }
   495  
   496  // delete-keys networkName tableName parallelWriters numberOfKeysEach
   497  func doDeleteKeys(ips []string, args []string) {
   498  	networkName := args[0]
   499  	tableName := args[1]
   500  	parallelWriters, _ := strconv.Atoi(args[2])
   501  	numberOfKeys, _ := strconv.Atoi(args[3])
   502  
   503  	doneCh := make(chan resultTuple, parallelWriters)
   504  	// Enable watch of tables from clients
   505  	for i := 0; i < parallelWriters; i++ {
   506  		go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh)
   507  	}
   508  	waitWriters(parallelWriters, false, doneCh)
   509  
   510  	// Start parallel writers that will create and delete unique keys
   511  	defer close(doneCh)
   512  	for i := 0; i < parallelWriters; i++ {
   513  		key := "key-" + strconv.Itoa(i) + "-"
   514  		log.G(context.TODO()).Infof("Spawn worker: %d on IP:%s", i, ips[i])
   515  		go deleteKeysNumber(ips[i], servicePort, networkName, tableName, key, numberOfKeys, doneCh)
   516  	}
   517  
   518  	// Sync with all the writers
   519  	keyMap := waitWriters(parallelWriters, true, doneCh)
   520  	log.G(context.TODO()).Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
   521  
   522  	// check table entries for 2 minutes
   523  	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute)
   524  	opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
   525  	cancel()
   526  	fmt.Fprintf(os.Stderr, "doDeletekeys succeeded in %d msec", opTime)
   527  }
   528  
   529  // write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec
   530  func doWriteDeleteUniqueKeys(ips []string, args []string) {
   531  	networkName := args[0]
   532  	tableName := args[1]
   533  	parallelWriters, _ := strconv.Atoi(args[2])
   534  	writeTimeSec, _ := strconv.Atoi(args[3])
   535  
   536  	doneCh := make(chan resultTuple, parallelWriters)
   537  	// Enable watch of tables from clients
   538  	for i := 0; i < parallelWriters; i++ {
   539  		go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh)
   540  	}
   541  	waitWriters(parallelWriters, false, doneCh)
   542  
   543  	// Start parallel writers that will create and delete unique keys
   544  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
   545  	for i := 0; i < parallelWriters; i++ {
   546  		key := "key-" + strconv.Itoa(i) + "-"
   547  		log.G(ctx).Infof("Spawn worker: %d on IP:%s", i, ips[i])
   548  		go writeDeleteUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
   549  	}
   550  
   551  	// Sync with all the writers
   552  	keyMap := waitWriters(parallelWriters, true, doneCh)
   553  	cancel()
   554  	log.G(ctx).Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
   555  
   556  	// check table entries for 2 minutes
   557  	ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
   558  	opDBTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
   559  	cancel()
   560  	ctx, cancel = context.WithTimeout(context.Background(), 30*time.Second)
   561  	opClientTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, clientTableEntriesNumber)
   562  	cancel()
   563  	fmt.Fprintf(os.Stderr, "doWriteDeleteUniqueKeys succeeded in %d msec and client %d msec", opDBTime, opClientTime)
   564  }
   565  
   566  // write-unique-keys networkName tableName numParallelWriters writeTimeSec
   567  func doWriteUniqueKeys(ips []string, args []string) {
   568  	networkName := args[0]
   569  	tableName := args[1]
   570  	parallelWriters, _ := strconv.Atoi(args[2])
   571  	writeTimeSec, _ := strconv.Atoi(args[3])
   572  
   573  	doneCh := make(chan resultTuple, parallelWriters)
   574  	// Enable watch of tables from clients
   575  	for i := 0; i < parallelWriters; i++ {
   576  		go clientWatchTable(ips[i], servicePort, networkName, tableName, doneCh)
   577  	}
   578  	waitWriters(parallelWriters, false, doneCh)
   579  
   580  	// Start parallel writers that will create and delete unique keys
   581  	defer close(doneCh)
   582  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
   583  	for i := 0; i < parallelWriters; i++ {
   584  		key := "key-" + strconv.Itoa(i) + "-"
   585  		log.G(ctx).Infof("Spawn worker: %d on IP:%s", i, ips[i])
   586  		go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
   587  	}
   588  
   589  	// Sync with all the writers
   590  	keyMap := waitWriters(parallelWriters, true, doneCh)
   591  	cancel()
   592  	log.G(ctx).Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
   593  
   594  	// check table entries for 2 minutes
   595  	ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
   596  	opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keyMap[totalWrittenKeys], dbTableEntriesNumber)
   597  	cancel()
   598  	fmt.Fprintf(os.Stderr, "doWriteUniqueKeys succeeded in %d msec", opTime)
   599  }
   600  
   601  // write-delete-leave-join networkName tableName numParallelWriters writeTimeSec
   602  func doWriteDeleteLeaveJoin(ips []string, args []string) {
   603  	networkName := args[0]
   604  	tableName := args[1]
   605  	parallelWriters, _ := strconv.Atoi(args[2])
   606  	writeTimeSec, _ := strconv.Atoi(args[3])
   607  
   608  	// Start parallel writers that will create and delete unique keys
   609  	doneCh := make(chan resultTuple, parallelWriters)
   610  	defer close(doneCh)
   611  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
   612  	for i := 0; i < parallelWriters; i++ {
   613  		key := "key-" + strconv.Itoa(i) + "-"
   614  		log.G(ctx).Infof("Spawn worker: %d on IP:%s", i, ips[i])
   615  		go writeDeleteLeaveJoin(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
   616  	}
   617  
   618  	// Sync with all the writers
   619  	keyMap := waitWriters(parallelWriters, true, doneCh)
   620  	cancel()
   621  	log.G(ctx).Infof("Written a total of %d keys on the cluster", keyMap["totalKeys"])
   622  
   623  	// check table entries for 2 minutes
   624  	ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
   625  	opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
   626  	cancel()
   627  	fmt.Fprintf(os.Stderr, "doWriteDeleteLeaveJoin succeeded in %d msec", opTime)
   628  }
   629  
   630  // write-delete-wait-leave-join networkName tableName numParallelWriters writeTimeSec
   631  func doWriteDeleteWaitLeaveJoin(ips []string, args []string) {
   632  	networkName := args[0]
   633  	tableName := args[1]
   634  	parallelWriters, _ := strconv.Atoi(args[2])
   635  	writeTimeSec, _ := strconv.Atoi(args[3])
   636  
   637  	// Start parallel writers that will create and delete unique keys
   638  	doneCh := make(chan resultTuple, parallelWriters)
   639  	defer close(doneCh)
   640  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
   641  	for i := 0; i < parallelWriters; i++ {
   642  		key := "key-" + strconv.Itoa(i) + "-"
   643  		log.G(ctx).Infof("Spawn worker: %d on IP:%s", i, ips[i])
   644  		go writeDeleteUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
   645  	}
   646  
   647  	// Sync with all the writers
   648  	keyMap := waitWriters(parallelWriters, true, doneCh)
   649  	cancel()
   650  	log.G(ctx).Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
   651  
   652  	// The writers will leave the network
   653  	for i := 0; i < parallelWriters; i++ {
   654  		log.G(ctx).Infof("worker leaveNetwork: %d on IP:%s", i, ips[i])
   655  		go leaveNetwork(ips[i], servicePort, networkName, doneCh)
   656  	}
   657  	waitWriters(parallelWriters, false, doneCh)
   658  
   659  	// Give some time
   660  	time.Sleep(100 * time.Millisecond)
   661  
   662  	// The writers will join the network
   663  	for i := 0; i < parallelWriters; i++ {
   664  		log.G(ctx).Infof("worker joinNetwork: %d on IP:%s", i, ips[i])
   665  		go joinNetwork(ips[i], servicePort, networkName, doneCh)
   666  	}
   667  	waitWriters(parallelWriters, false, doneCh)
   668  
   669  	// check table entries for 2 minutes
   670  	ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
   671  	opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
   672  	cancel()
   673  	fmt.Fprintf(os.Stderr, "doWriteDeleteWaitLeaveJoin succeeded in %d msec", opTime)
   674  }
   675  
   676  // write-wait-leave networkName tableName numParallelWriters writeTimeSec
   677  func doWriteWaitLeave(ips []string, args []string) {
   678  	networkName := args[0]
   679  	tableName := args[1]
   680  	parallelWriters, _ := strconv.Atoi(args[2])
   681  	writeTimeSec, _ := strconv.Atoi(args[3])
   682  
   683  	// Start parallel writers that will create and delete unique keys
   684  	doneCh := make(chan resultTuple, parallelWriters)
   685  	defer close(doneCh)
   686  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
   687  	for i := 0; i < parallelWriters; i++ {
   688  		key := "key-" + strconv.Itoa(i) + "-"
   689  		log.G(ctx).Infof("Spawn worker: %d on IP:%s", i, ips[i])
   690  		go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
   691  	}
   692  
   693  	// Sync with all the writers
   694  	keyMap := waitWriters(parallelWriters, true, doneCh)
   695  	cancel()
   696  	log.G(ctx).Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
   697  
   698  	// The writers will leave the network
   699  	for i := 0; i < parallelWriters; i++ {
   700  		log.G(ctx).Infof("worker leaveNetwork: %d on IP:%s", i, ips[i])
   701  		go leaveNetwork(ips[i], servicePort, networkName, doneCh)
   702  	}
   703  	waitWriters(parallelWriters, false, doneCh)
   704  
   705  	// check table entries for 2 minutes
   706  	ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
   707  	opTime := checkTable(ctx, ips, servicePort, networkName, tableName, 0, dbTableEntriesNumber)
   708  	cancel()
   709  	fmt.Fprintf(os.Stderr, "doWriteLeaveJoin succeeded in %d msec", opTime)
   710  }
   711  
   712  // write-wait-leave-join networkName tableName numParallelWriters writeTimeSec numParallelLeaver
   713  func doWriteWaitLeaveJoin(ips []string, args []string) {
   714  	networkName := args[0]
   715  	tableName := args[1]
   716  	parallelWriters, _ := strconv.Atoi(args[2])
   717  	writeTimeSec, _ := strconv.Atoi(args[3])
   718  	parallelLeaver, _ := strconv.Atoi(args[4])
   719  
   720  	// Start parallel writers that will create and delete unique keys
   721  	doneCh := make(chan resultTuple, parallelWriters)
   722  	defer close(doneCh)
   723  	ctx, cancel := context.WithTimeout(context.Background(), time.Duration(writeTimeSec)*time.Second)
   724  	for i := 0; i < parallelWriters; i++ {
   725  		key := "key-" + strconv.Itoa(i) + "-"
   726  		log.G(ctx).Infof("Spawn worker: %d on IP:%s", i, ips[i])
   727  		go writeUniqueKeys(ctx, ips[i], servicePort, networkName, tableName, key, doneCh)
   728  	}
   729  
   730  	// Sync with all the writers
   731  	keyMap := waitWriters(parallelWriters, true, doneCh)
   732  	cancel()
   733  	log.G(ctx).Infof("Written a total of %d keys on the cluster", keyMap[totalWrittenKeys])
   734  
   735  	keysExpected := keyMap[totalWrittenKeys]
   736  	// The Leavers will leave the network
   737  	for i := 0; i < parallelLeaver; i++ {
   738  		log.G(ctx).Infof("worker leaveNetwork: %d on IP:%s", i, ips[i])
   739  		go leaveNetwork(ips[i], servicePort, networkName, doneCh)
   740  		// Once a node leave all the keys written previously will be deleted, so the expected keys will consider that as removed
   741  		keysExpected -= keyMap[ips[i]]
   742  	}
   743  	waitWriters(parallelLeaver, false, doneCh)
   744  
   745  	// Give some time
   746  	time.Sleep(100 * time.Millisecond)
   747  
   748  	// The writers will join the network
   749  	for i := 0; i < parallelLeaver; i++ {
   750  		log.G(ctx).Infof("worker joinNetwork: %d on IP:%s", i, ips[i])
   751  		go joinNetwork(ips[i], servicePort, networkName, doneCh)
   752  	}
   753  	waitWriters(parallelLeaver, false, doneCh)
   754  
   755  	// check table entries for 2 minutes
   756  	ctx, cancel = context.WithTimeout(context.Background(), 2*time.Minute)
   757  	opTime := checkTable(ctx, ips, servicePort, networkName, tableName, keysExpected, dbTableEntriesNumber)
   758  	cancel()
   759  	fmt.Fprintf(os.Stderr, "doWriteWaitLeaveJoin succeeded in %d msec", opTime)
   760  }
   761  
   762  var cmdArgChec = map[string]int{
   763  	"debug":                    0,
   764  	"fail":                     0,
   765  	"ready":                    2,
   766  	"join":                     2,
   767  	"leave":                    2,
   768  	"join-network":             3,
   769  	"leave-network":            3,
   770  	"cluster-peers":            5,
   771  	"network-peers":            5,
   772  	"write-delete-unique-keys": 7,
   773  }
   774  
   775  // Client is a client
   776  func Client(args []string) {
   777  	log.G(context.TODO()).Infof("[CLIENT] Starting with arguments %v", args)
   778  	command := args[0]
   779  
   780  	if len(args) < cmdArgChec[command] {
   781  		log.G(context.TODO()).Fatalf("Command %s requires %d arguments, passed %d, aborting...", command, cmdArgChec[command], len(args))
   782  	}
   783  
   784  	switch command {
   785  	case "debug":
   786  		time.Sleep(1 * time.Hour)
   787  		os.Exit(0)
   788  	case "fail":
   789  		log.G(context.TODO()).Fatalf("Test error condition with message: error error error")
   790  	}
   791  
   792  	serviceName := args[1]
   793  	ips, _ := net.LookupHost("tasks." + serviceName)
   794  	log.G(context.TODO()).Infof("got the ips %v", ips)
   795  	if len(ips) == 0 {
   796  		log.G(context.TODO()).Fatalf("Cannot resolve any IP for the service tasks.%s", serviceName)
   797  	}
   798  	servicePort = args[2]
   799  	commandArgs := args[3:]
   800  	log.G(context.TODO()).Infof("Executing %s with args:%v", command, commandArgs)
   801  	switch command {
   802  	case "ready":
   803  		doReady(ips)
   804  	case "join":
   805  		doJoin(ips)
   806  	case "leave":
   807  
   808  	case "cluster-peers":
   809  		// cluster-peers maxRetry
   810  		doClusterPeers(ips, commandArgs)
   811  
   812  	case "join-network":
   813  		// join-network networkName
   814  		doJoinNetwork(ips, commandArgs)
   815  	case "leave-network":
   816  		// leave-network networkName
   817  		doLeaveNetwork(ips, commandArgs)
   818  	case "network-peers":
   819  		// network-peers networkName expectedNumberPeers maxRetry
   820  		doNetworkPeers(ips, commandArgs)
   821  		//	case "network-stats-entries":
   822  		//		// network-stats-entries networkName maxRetry
   823  		//		doNetworkPeers(ips, commandArgs)
   824  	case "network-stats-queue":
   825  		// network-stats-queue networkName <lt/gt> queueSize
   826  		doNetworkStatsQueue(ips, commandArgs)
   827  
   828  	case "write-keys":
   829  		// write-keys networkName tableName parallelWriters numberOfKeysEach
   830  		doWriteKeys(ips, commandArgs)
   831  	case "delete-keys":
   832  		// delete-keys networkName tableName parallelWriters numberOfKeysEach
   833  		doDeleteKeys(ips, commandArgs)
   834  	case "write-unique-keys":
   835  		// write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec
   836  		doWriteUniqueKeys(ips, commandArgs)
   837  	case "write-delete-unique-keys":
   838  		// write-delete-unique-keys networkName tableName numParallelWriters writeTimeSec
   839  		doWriteDeleteUniqueKeys(ips, commandArgs)
   840  	case "write-delete-leave-join":
   841  		// write-delete-leave-join networkName tableName numParallelWriters writeTimeSec
   842  		doWriteDeleteLeaveJoin(ips, commandArgs)
   843  	case "write-delete-wait-leave-join":
   844  		// write-delete-wait-leave-join networkName tableName numParallelWriters writeTimeSec
   845  		doWriteDeleteWaitLeaveJoin(ips, commandArgs)
   846  	case "write-wait-leave":
   847  		// write-wait-leave networkName tableName numParallelWriters writeTimeSec
   848  		doWriteWaitLeave(ips, commandArgs)
   849  	case "write-wait-leave-join":
   850  		// write-wait-leave networkName tableName numParallelWriters writeTimeSec
   851  		doWriteWaitLeaveJoin(ips, commandArgs)
   852  	default:
   853  		log.G(context.TODO()).Fatalf("Command %s not recognized", command)
   854  	}
   855  }