github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/libnetwork/cmd/networkdb-test/dbclient/ndbClient.go (about)

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