Beispiel #1
0
func TestRepeatSim(t *testing.T) {
	clk := loadsim.NewSimClock()

	req, err := http.NewRequest("GET", "", nil)
	if err != nil {
		log.Fatal(err)
	}
	agents := []loadsim.Agent{
		&loadsim.RepeatAgent{
			BaseRequest: req,
			Clock:       clk,
		},
	}

	worker := makeSimWorker(clk)

	stop := make(chan struct{})
	resCh := loadsim.Simulate(agents, worker, clk, 10*time.Second)
	go clk.Run(stop)

	results := collectResults(resCh)[""]
	close(stop)

	if err := assertStatus(results, http.StatusOK); err != nil {
		t.Error(err)
	}
	if err := assertWorkDurations(results, 100*time.Millisecond, time.Millisecond); err != nil {
		t.Error(err)
	}

	count := len(results)
	if count < 99 || count > 101 {
		t.Errorf("expected 99-101 requests, got %d", count)
	}
}
Beispiel #2
0
func TestPoolSim(t *testing.T) {
	clk := loadsim.NewSimClock()

	req, err := http.NewRequest("GET", "", nil)
	if err != nil {
		log.Fatal(err)
	}
	agents := []loadsim.Agent{
		&loadsim.RepeatAgent{
			BaseRequest: req,
			Clock:       clk,
			ID:          "repeat1",
		},
		&loadsim.RepeatAgent{
			BaseRequest: req,
			Clock:       clk,
			ID:          "repeat2",
		},
		&loadsim.IntervalAgent{
			BaseRequest: req,
			Clock:       clk,
			Interval:    510 * time.Millisecond,
			ID:          "interval",
		},
	}

	worker := &loadsim.WorkerPool{
		Backlog: 10,
		Timeout: 200 * time.Millisecond,
		Workers: []loadsim.Worker{makeSimWorker(clk), makeSimWorker(clk)},
		Clock:   clk,
	}

	stop := make(chan struct{})
	resCh := loadsim.Simulate(agents, worker, clk, 10*time.Second)
	go clk.Run(stop)

	agentResults := collectResults(resCh)
	close(stop)

	for id, results := range agentResults {
		if err := assertStatus(results, http.StatusOK); err != nil {
			t.Errorf("%s: %s", id, err)
		}
		if err := assertWorkDurations(results, 100*time.Millisecond, time.Millisecond); err != nil {
			t.Errorf("%s: %s", id, err)
		}

		count := len(results)
		switch id {
		case "repeat1", "repeat2":
			if count < 85 || count > 95 {
				t.Errorf("%s: expected 85-95 requests, got %d", id, count)
			}
		case "interval":
			if count != 20 {
				t.Errorf("%s: expected 20 requests, got %d", id, count)
			}
		}
	}
}
Beispiel #3
0
func listOverload() {
	var keys []string
	if envKeys := os.Getenv("STRIPE_KEYS"); envKeys != "" {
		keys = strings.Split(envKeys, ",")
	} else {
		keys = []string{"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P"}
	}

	start := time.Now()
	workerCnt := 15
	cfg := WorkerConfig{
		Duration:       60 * time.Second,
		Workers:        workerCnt,
		CPUs:           2,
		Hosts:          2,
		Backlog:        200,
		Timeout:        40 * time.Second,
		WallClockBurst: time.Duration(5*workerCnt) * time.Second,
		WallClockRate:  time.Duration(workerCnt) * time.Second,
		ResourceMapper: func(req *http.Request) []loadsim.ResourceNeed {
			if req.Method == "GET" {
				return []loadsim.ResourceNeed{
					{"time", 500},
					{"CPU", 2000},
				}
			}

			return []loadsim.ResourceNeed{
				{"time", 1200},
				{"CPU", 400},
			}
		},
	}
	prefix := "data"

	simClock := loadsim.NewSimClock()

	runners := []struct {
		Name  string
		Run   func(WorkerConfig, []loadsim.Agent, loadsim.Clock) []loadsim.Result
		Clock loadsim.Clock
	}{
		{"sim", simRun, simClock},
		//{"http", httpRun, &loadsim.WallClock{}},
	}

	listreq, err := http.NewRequest("GET", "https://qa-api.stripe.com/v1/charges?limit=100", nil)
	if err != nil {
		log.Fatal(err)
	}
	listreq.SetBasicAuth(keys[0], "")

	for _, listAgents := range []int{1, 10, 60} {
		for _, createInterval := range []time.Duration{0, 3 * time.Second} {
			var rep string
			if createInterval == 0 {
				rep = "repeat"
			} else {
				rep = createInterval.String()
			}

			for _, runner := range runners {
				var agents []loadsim.Agent

				for i := 0; i < listAgents; i++ {
					id := fmt.Sprintf("%s %s", listreq.Method, listreq.URL.Path)
					delay := 20*time.Second + time.Millisecond*time.Duration(i*300)

					agents = append(agents, &loadsim.DelayLimitAgent{
						Agent: buildAgent(runner.Clock, id, listreq, nil, 0),
						Delay: delay,
						Limit: 60 * time.Second,
						Clock: runner.Clock,
					})
				}

				for i := 0; i < 6; i++ {
					req, body := chargeCreate(keys[i+1])
					id := fmt.Sprintf("%s %s", req.Method, req.URL.Path)
					agents = append(agents, buildAgent(runner.Clock, id, req, body, createInterval))
				}

				results := runner.Run(cfg, agents, runner.Clock)

				path := path.Join(prefix, fmt.Sprintf("%d_%s_%dlist@%s.tsv", start.Unix(), runner.Name, listAgents, rep))
				if err := writeResults(results, path); err != nil {
					log.Fatal(err)
				}
			}
		}
	}
}
Beispiel #4
0
func TestSimClockSimple(t *testing.T) {
	stop := make(chan struct{})
	clk := loadsim.NewSimClock()

	go clk.Run(stop)

	// Test a simple clock advancing
	ticker1, tickStop1 := clk.Tick()

	start, err := timeoutRead(ticker1)
	if err != nil {
		t.Fatal(err)
	}
	next, err := timeoutRead(ticker1)
	if err != nil {
		t.Fatal(err)
	}

	if next.Sub(start) != time.Millisecond {
		t.Fatalf("clock advanced from %s by %s, not 1ms", start, next.Sub(start))
	}

	ticker2, tickStop2 := clk.Tick()
	<-ticker1

	// With the other clock started, we should read an equal number
	// of ticks from both clocks but we can't be sure which we started
	// from.

	var now1, now2 time.Time
	var count1, count2 int
	for i := 0; i < 10; i++ {
		select {
		case next := <-ticker1:
			if !(now1.IsZero() || next.Sub(now1) == time.Millisecond) {
				t.Fatalf("clock1 advanced from %s to %s, not 1ms", now1, next)
			}
			now1 = next
			count1++
		case next := <-ticker2:
			if !(now2.IsZero() || next.Sub(now2) == time.Millisecond) {
				t.Fatalf("clock2 advanced from %s to %s, not 1ms", now1, next)
			}
			now2 = next
			count2++
		case <-time.After(time.Millisecond):
			t.Fatalf("failed to read from ticker")
		}
	}

	if count1 != count2 {
		t.Fatalf("unequal clock advances, %d != %d", count1, count2)
	}

	if diff := now1.Sub(now2); diff > time.Millisecond || diff < -time.Millisecond {
		t.Fatalf("clocks diverged, %s !~ %s", now1, now2)
	}

	// Ensure we're blocking on ticker1
	select {
	case <-ticker2:
	default:
	}
	select {
	case <-ticker2:
		t.Fatalf("should be blocking on ticker1")
	default:
	}

	// Stop ticker1 and ensure this unblocks us
	close(tickStop1)
	_, err = timeoutRead(ticker2)
	if err != nil {
		t.Fatalf("should have unblocked ticker2")
	}

	close(tickStop2)
}
Beispiel #5
0
func workerCounts() {
	start := time.Now()
	cpus := 4
	hosts := 4
	duration := 180 * time.Second
	fuckeryDelay := duration / 6
	fuckeryDuration := duration - fuckeryDelay*2
	cfg := WorkerConfig{
		Duration: duration,
		CPUs:     cpus,
		Hosts:    hosts,
		Backlog:  hosts * 100,
		Timeout:  40 * time.Second,
	}
	prefix := "data"

	clk := loadsim.NewSimClock()

	normalMapper := func(req *http.Request) []loadsim.ResourceNeed {
		if req.Method == "GET" {
			total := 3000
			wall := total / 5
			return []loadsim.ResourceNeed{
				{"time", wall},
				{"CPU", total - wall},
			}
		}

		total := 1350
		cpu := total / 3
		return []loadsim.ResourceNeed{
			{"time", total - cpu},
			{"CPU", cpu},
		}
	}

	listreq, err := http.NewRequest("GET", "https://qa-api.stripe.com/v1/charges?limit=100", nil)
	if err != nil {
		log.Fatal(err)
	}
	listreq.SetBasicAuth("list", "")

	var listAgents []loadsim.Agent
	for i := 0; i < (hosts * cpus * 2); i++ {
		listAgents = append(listAgents, buildAgent(clk, "list", listreq, nil, 0))
	}

	kerfuckery := []struct {
		Name           string
		Agents         []loadsim.Agent
		ResourceMapper loadsim.ResourceMapper
	}{
		//{"none", nil, nil},

		// 10% of charges see a 20 second delay in the response
		/*{
			"charge-net-delay",
			nil,
			func(req *http.Request) []loadsim.ResourceNeed {
				needs := normalMapper(req)
				if req.Method == "POST" && req.URL.Path == "/v1/charges" && rand.Float32() < 0.1 {
					needs = append(needs, &loadsim.ResourceNeed{"time", 20000})
				}
				return needs
			},
		},*/

		// 100% of charges see a 1.25 second delay in the response
		/*{
			"charge-scoring-delay",
			nil,
			func(req *http.Request) []loadsim.ResourceNeed {
				needs := normalMapper(req)
				if req.Method == "POST" && req.URL.Path == "/v1/charges" {
					needs = append(needs, &loadsim.ResourceNeed{"time", 1250})
				}
				return needs
			},
		},*/

		// A bunch of expensive list queries wreck havock
		{"list", listAgents, nil},
	}

	for _, workerCnt := range []int{cpus + cpus/2, 2 * cpus, 4 * cpus, 8 * cpus} {
		cfg.Workers = workerCnt
		cfg.WallClockRate = time.Duration(workerCnt*hosts) / 2 * time.Second
		cfg.WallClockBurst = cfg.WallClockRate * 5

		for _, fuckery := range kerfuckery {
			var agents []loadsim.Agent

			for i := 0; i < hosts*cpus*3/2; i++ {
				req, body := chargeCreate(strconv.Itoa(i))
				agents = append(agents, buildAgent(clk, "charge", req, body, 3*time.Second))
			}

			for i, newAgent := range fuckery.Agents {
				agents = append(agents, &loadsim.DelayLimitAgent{
					Agent: newAgent,
					Delay: fuckeryDelay + duration/1000*time.Duration(i),
					Limit: duration - fuckeryDelay*2,
					Clock: clk,
				})
			}
			if fuckery.ResourceMapper != nil {
				beginFuckery := clk.Now().Add(fuckeryDelay)
				endFuckery := beginFuckery.Add(fuckeryDuration)
				cfg.ResourceMapper = func(req *http.Request) []loadsim.ResourceNeed {
					now := clk.Now()
					if now.After(beginFuckery) && now.Before(endFuckery) {
						return fuckery.ResourceMapper(req)
					}

					return normalMapper(req)
				}
			} else {
				cfg.ResourceMapper = normalMapper
			}

			results := simRun(cfg, agents, clk)

			path := path.Join(prefix, fmt.Sprintf("%d_%dworkers_%s.tsv", start.Unix(), workerCnt*hosts, fuckery.Name))
			if err := writeResults(results, path); err != nil {
				log.Fatal(err)
			}
		}
	}
}
Beispiel #6
0
func qaListOverload() {
	start := time.Now()
	workerCnt := 15
	hosts := 2
	cpus := 2
	cfg := WorkerConfig{
		Duration: 210 * time.Second,
		Workers:  workerCnt,
		CPUs:     cpus,
		Hosts:    hosts,
		Backlog:  100 * hosts,
		Timeout:  40 * time.Second,
		ResourceMapper: func(req *http.Request) []loadsim.ResourceNeed {
			if req.Method == "GET" {
				return []loadsim.ResourceNeed{
					{"time", 500},
					{"CPU", 2000},
				}
			}

			return []loadsim.ResourceNeed{
				{"time", 1200},
				{"CPU", 400},
			}
		},
		RequestOverhead: []loadsim.ResourceNeed{
			{"time", 20},
			{"CPU", 20},
		},
	}
	prefix := "data"

	clk := loadsim.NewSimClock()

	listreq, err := http.NewRequest("GET", "https://qa-api.stripe.com/v1/charges?limit=100", nil)
	if err != nil {
		log.Fatal(err)
	}
	listreq.SetBasicAuth("list", "")

	var agents []loadsim.Agent

	for i := 0; i < 60; i++ {
		id := fmt.Sprintf("%s %s", listreq.Method, listreq.URL.Path)
		delay := 30*time.Second + time.Millisecond*time.Duration(i*200)

		agents = append(agents, &loadsim.DelayLimitAgent{
			Agent: buildAgent(clk, id, listreq, nil, 0),
			Delay: delay,
			Limit: 120 * time.Second,
			Clock: clk,
		})
	}

	for i := 0; i < 6; i++ {
		id := fmt.Sprintf("charge%d", i)
		req, body := chargeCreate(id)
		agents = append(agents, buildAgent(clk, id, req, body, 0))
	}

	cases := []struct {
		Name          string
		WallClockRate time.Duration
	}{
		{"no-limiter", 0},
		{"limiter", time.Duration(workerCnt*hosts/2) * time.Second},
	}

	for _, simcase := range cases {
		cfg.WallClockRate = simcase.WallClockRate
		cfg.WallClockBurst = 5 * cfg.WallClockRate

		results := simRun(cfg, agents, clk)

		path := path.Join(prefix, fmt.Sprintf("%d_%s_qalist.tsv", start.Unix(), simcase.Name))
		if err := writeResults(results, path); err != nil {
			log.Fatal(err)
		}
	}
}