github.com/unigraph-dev/dgraph@v1.1.1-0.20200923154953-8b52b426f765/contrib/embargo/main.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"log"
     8  	"math/rand"
     9  	"os"
    10  	"os/exec"
    11  	"strings"
    12  	"time"
    13  
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  var ctxb = context.Background()
    18  
    19  func run(ctx context.Context, command string) error {
    20  	args := strings.Split(command, " ")
    21  	var checkedArgs []string
    22  	for _, arg := range args {
    23  		if len(arg) > 0 {
    24  			checkedArgs = append(checkedArgs, arg)
    25  		}
    26  	}
    27  	cmd := exec.CommandContext(ctx, checkedArgs[0], checkedArgs[1:]...)
    28  	var out bytes.Buffer
    29  	cmd.Stdout = &out
    30  	cmd.Stderr = &out
    31  	if err := cmd.Run(); err != nil {
    32  		fmt.Printf("[%v] ERROR. Command %q. Error: %v. Output:\n%s\n",
    33  			time.Now().UTC(), command, err, out.String())
    34  		return err
    35  	}
    36  	fmt.Printf("[%v] Command %q. Output:\n%s\n", time.Now().UTC(), command, out.String())
    37  	return nil
    38  }
    39  
    40  func increment(atLeast int, args string) error {
    41  	errCh := make(chan error, 1)
    42  	ctx, cancel := context.WithTimeout(ctxb, 1*time.Minute)
    43  	defer cancel()
    44  
    45  	addrs := []string{"localhost:9180", "localhost:9182", "localhost:9183"}
    46  	for _, addr := range addrs {
    47  		go func(addr string) {
    48  			errCh <- run(ctx, fmt.Sprintf("dgraph increment --alpha=%s %s", addr, args))
    49  		}(addr)
    50  	}
    51  	start := time.Now()
    52  	var ok int
    53  	for i := 0; i < len(addrs) && ok < atLeast; i++ {
    54  		if err := <-errCh; err == nil {
    55  			ok++
    56  		} else {
    57  			fmt.Printf("[%v] Got error during increment: %v\n", time.Now().UTC(), err)
    58  		}
    59  	}
    60  	if ok < atLeast {
    61  		return errors.Errorf("Increment with atLeast=%d failed. OK: %d", atLeast, ok)
    62  	}
    63  	dur := time.Since(start).Round(time.Millisecond)
    64  	fmt.Printf("\n[%v] ===> TIME taken to converge %d alphas: %s\n\n",
    65  		time.Now().UTC(), atLeast, dur)
    66  	return nil
    67  }
    68  
    69  func getStatus(zero string) error {
    70  	cmd := exec.Command("http", "GET", fmt.Sprintf("%s/state", zero))
    71  	var out bytes.Buffer
    72  	cmd.Stdout = &out
    73  	cmd.Stderr = &out
    74  	if err := cmd.Run(); err != nil {
    75  		fmt.Printf("ERROR. Status at %s. Error: %v. Output:\n%s\n", zero, err, out.String())
    76  		return err
    77  	}
    78  	output := out.String()
    79  	if strings.Contains(output, "errors") {
    80  		fmt.Printf("ERROR. Status at %s. Output:\n%s\n", zero, output)
    81  		return errors.Errorf(output)
    82  	}
    83  	// var m map[string]interface{}
    84  	// if err := json.Unmarshal([]byte(output), &m); err != nil {
    85  	// 	return err
    86  	// }
    87  	// pretty, err := json.MarshalIndent(m, "", "  ")
    88  	// if err != nil {
    89  	// 	return err
    90  	// }
    91  	fmt.Printf("Status at %s:\n%s\n", zero, output)
    92  	return nil
    93  }
    94  
    95  func testCommon(remove, join, incrementArgs string, nodes []string, minAlphasUp int) error {
    96  	fmt.Printf("Nodes: %+v\n", nodes)
    97  	for _, node := range nodes {
    98  		if err := getStatus("localhost:6080"); err != nil {
    99  			return err
   100  		}
   101  		fmt.Printf("\n==> Remove cmd %q on NODES: %s\n", remove, node)
   102  		if err := run(ctxb, remove+" "+node); err != nil {
   103  			return err
   104  		}
   105  		if err := run(ctxb, "embargo status"); err != nil {
   106  			return err
   107  		}
   108  		if err := increment(minAlphasUp, incrementArgs); err != nil {
   109  			return err
   110  		}
   111  		// Then join, if available.
   112  		if len(join) == 0 {
   113  			continue
   114  		}
   115  		if err := run(ctxb, join); err != nil {
   116  			return err
   117  		}
   118  		if err := increment(3, incrementArgs); err != nil {
   119  			return err
   120  		}
   121  	}
   122  	return nil
   123  }
   124  
   125  func waitForHealthy() error {
   126  	for _, zero := range []string{"localhost:6080", "localhost:6082", "localhost:6083"} {
   127  		if err := getStatus(zero); err != nil {
   128  			return err
   129  		}
   130  	}
   131  	for _, alpha := range []string{"localhost:9180", "localhost:9182", "localhost:9183"} {
   132  		if err := run(ctxb, "dgraph increment --alpha="+alpha); err != nil {
   133  			return err
   134  		}
   135  	}
   136  	return nil
   137  }
   138  
   139  func runTests() error {
   140  	for {
   141  		if err := waitForHealthy(); err != nil {
   142  			fmt.Printf("Error while waitForHealthy: %v\n.", err)
   143  			time.Sleep(5 * time.Second)
   144  			fmt.Println("Retrying...")
   145  		} else {
   146  			break
   147  		}
   148  	}
   149  
   150  	var nodes []string
   151  	for i := 1; i <= 3; i++ {
   152  		for j := 1; j <= 3; j++ {
   153  			nodes = append(nodes, fmt.Sprintf("zero%d dg%d", i, j))
   154  		}
   155  	}
   156  
   157  	var alphaNodes []string
   158  	for i := 1; i <= 3; i++ {
   159  		alphaNodes = append(alphaNodes, fmt.Sprintf("dg%d", i))
   160  	}
   161  
   162  	// Setting flaky --all just does not converge. Too many network interruptions.
   163  	// if err := testCommon("embargo flaky", "embargo fast --all", 3); err != nil {
   164  	// 	fmt.Printf("Error testFlaky: %v\n", err)
   165  	// 	return err
   166  	// }
   167  	// fmt.Println("===> Flaky TEST: OK")
   168  
   169  	// if err := testCommon("embargo slow", "embargo fast --all", 3); err != nil {
   170  	// 	fmt.Printf("Error testSlow: %v\n", err)
   171  	// 	return err
   172  	// }
   173  	// fmt.Println("===> Slow TEST: OK")
   174  
   175  	if err := testCommon("embargo stop", "embargo start --all", "", nodes, 2); err != nil {
   176  		fmt.Printf("Error testStop: %v\n", err)
   177  		return err
   178  	}
   179  	fmt.Println("===> Stop TEST: OK")
   180  
   181  	if err := testCommon("embargo restart", "", "", nodes, 3); err != nil {
   182  		fmt.Printf("Error testRestart with restart: %v\n", err)
   183  		return err
   184  	}
   185  	fmt.Println("===> Restart TEST: OK")
   186  
   187  	if err := testCommon("embargo partition", "embargo join", "", nodes, 2); err != nil {
   188  		fmt.Printf("Error testPartitions: %v\n", err)
   189  		return err
   190  	}
   191  	fmt.Println("===> Partition TEST: OK")
   192  
   193  	if err := testCommon("embargo partition", "embargo join", "--be", alphaNodes, 3); err != nil {
   194  		fmt.Printf("Error testPartitionsBestEffort: %v\n", err)
   195  		return err
   196  	}
   197  	fmt.Println("===> Partition best-effort TEST: OK")
   198  
   199  	return nil
   200  }
   201  
   202  func main() {
   203  	rand.Seed(time.Now().UnixNano())
   204  	fmt.Println("Starting embargo")
   205  	if err := run(ctxb, "embargo up"); err != nil {
   206  		log.Fatal(err)
   207  	}
   208  	// This defer can be moved within runTests, if we want to destroy embargo,
   209  	// in case our tests fail. We don't want to do that, because then we won't
   210  	// be able to get the logs.
   211  	defer func() {
   212  		if err := run(ctxb, "embargo destroy"); err != nil {
   213  			log.Fatalf("While destroying: %v", err)
   214  		}
   215  	}()
   216  
   217  	if err := runTests(); err != nil {
   218  		os.Exit(1)
   219  	}
   220  	fmt.Println("embargo tests: OK")
   221  }