|
|
@@ -19,6 +19,7 @@ import (
|
|
|
"errors"
|
|
|
"fmt"
|
|
|
"io/ioutil"
|
|
|
+ "math/rand"
|
|
|
"net/http"
|
|
|
"path/filepath"
|
|
|
"strings"
|
|
|
@@ -234,6 +235,33 @@ func NewCluster(logger *zap.Logger, fpath string) (*Cluster, error) {
|
|
|
}
|
|
|
go clus.serveTesterServer()
|
|
|
|
|
|
+ clus.updateFailures()
|
|
|
+
|
|
|
+ clus.rateLimiter = rate.NewLimiter(
|
|
|
+ rate.Limit(int(clus.Tester.StressQPS)),
|
|
|
+ int(clus.Tester.StressQPS),
|
|
|
+ )
|
|
|
+ clus.updateStresserChecker()
|
|
|
+ return clus, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (clus *Cluster) serveTesterServer() {
|
|
|
+ clus.logger.Info(
|
|
|
+ "started tester HTTP server",
|
|
|
+ zap.String("tester-address", clus.Tester.TesterAddr),
|
|
|
+ )
|
|
|
+ err := clus.testerHTTPServer.ListenAndServe()
|
|
|
+ clus.logger.Info(
|
|
|
+ "tester HTTP server returned",
|
|
|
+ zap.String("tester-address", clus.Tester.TesterAddr),
|
|
|
+ zap.Error(err),
|
|
|
+ )
|
|
|
+ if err != nil && err != http.ErrServerClosed {
|
|
|
+ clus.logger.Fatal("tester HTTP errored", zap.Error(err))
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (clus *Cluster) updateFailures() {
|
|
|
for _, cs := range clus.Tester.FailureCases {
|
|
|
switch cs {
|
|
|
case "KILL_ONE_FOLLOWER":
|
|
|
@@ -270,33 +298,51 @@ func NewCluster(logger *zap.Logger, fpath string) (*Cluster, error) {
|
|
|
clus.failures = append(clus.failures, newFailureNoOp())
|
|
|
case "EXTERNAL":
|
|
|
clus.failures = append(clus.failures, newFailureExternal(clus.Tester.ExternalExecPath))
|
|
|
- default:
|
|
|
- return nil, fmt.Errorf("unknown failure %q", cs)
|
|
|
}
|
|
|
}
|
|
|
+}
|
|
|
|
|
|
- clus.rateLimiter = rate.NewLimiter(
|
|
|
- rate.Limit(int(clus.Tester.StressQPS)),
|
|
|
- int(clus.Tester.StressQPS),
|
|
|
- )
|
|
|
- clus.updateStresserChecker()
|
|
|
- return clus, nil
|
|
|
+func (clus *Cluster) shuffleFailures() {
|
|
|
+ rand.Seed(time.Now().UnixNano())
|
|
|
+ offset := rand.Intn(1000)
|
|
|
+ n := len(clus.failures)
|
|
|
+ cp := coprime(n)
|
|
|
+
|
|
|
+ clus.logger.Info("shuffling test failure cases", zap.Int("total", n))
|
|
|
+ fs := make([]Failure, n)
|
|
|
+ for i := 0; i < n; i++ {
|
|
|
+ fs[i] = clus.failures[(cp*i+offset)%n]
|
|
|
+ }
|
|
|
+ clus.failures = fs
|
|
|
+ clus.logger.Info("shuffled test failure cases", zap.Int("total", n))
|
|
|
}
|
|
|
|
|
|
-func (clus *Cluster) serveTesterServer() {
|
|
|
- clus.logger.Info(
|
|
|
- "started tester HTTP server",
|
|
|
- zap.String("tester-address", clus.Tester.TesterAddr),
|
|
|
- )
|
|
|
- err := clus.testerHTTPServer.ListenAndServe()
|
|
|
- clus.logger.Info(
|
|
|
- "tester HTTP server returned",
|
|
|
- zap.String("tester-address", clus.Tester.TesterAddr),
|
|
|
- zap.Error(err),
|
|
|
- )
|
|
|
- if err != nil && err != http.ErrServerClosed {
|
|
|
- clus.logger.Fatal("tester HTTP errored", zap.Error(err))
|
|
|
+/*
|
|
|
+x and y of GCD 1 are coprime to each other
|
|
|
+
|
|
|
+x1 = ( coprime of n * idx1 + offset ) % n
|
|
|
+x2 = ( coprime of n * idx2 + offset ) % n
|
|
|
+(x2 - x1) = coprime of n * (idx2 - idx1) % n
|
|
|
+ = (idx2 - idx1) = 1
|
|
|
+
|
|
|
+Consecutive x's are guaranteed to be distinct
|
|
|
+*/
|
|
|
+func coprime(n int) int {
|
|
|
+ coprime := 1
|
|
|
+ for i := n / 2; i < n; i++ {
|
|
|
+ if gcd(i, n) == 1 {
|
|
|
+ coprime = i
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return coprime
|
|
|
+}
|
|
|
+
|
|
|
+func gcd(x, y int) int {
|
|
|
+ if y == 0 {
|
|
|
+ return x
|
|
|
}
|
|
|
+ return gcd(y, x%y)
|
|
|
}
|
|
|
|
|
|
func (clus *Cluster) updateStresserChecker() {
|