<commit_before>// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package gvisor provides support for gVisor, user-space kernel, testing.
// See https://github.com/google/gvisor
package gvisor

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/google/syzkaller/pkg/config"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/vm/vmimpl"
)

func init() {
	vmimpl.Register("gvisor", ctor, true)
}

type Config struct {
	Count     int    `json:"count"` // number of VMs to use
	RunscArgs string `json:"runsc_args"`
}

type Pool struct {
	env *vmimpl.Env
	cfg *Config
}

type instance struct {
	cfg      *Config
	image    string
	debug    bool
	rootDir  string
	imageDir string
	name     string
	port     int
	cmd      *exec.Cmd
	merger   *vmimpl.OutputMerger
}

func ctor(env *vmimpl.Env) (vmimpl.Pool, error) {
	cfg := &Config{
		Count: 1,
	}
	if err := config.LoadData(env.Config, cfg); err != nil {
		return nil, fmt.Errorf("failed to parse vm config: %v", err)
	}
	if cfg.Count < 1 || cfg.Count > 128 {
		return nil, fmt.Errorf("invalid config param count: %v, want [1, 128]", cfg.Count)
	}
	if env.Debug && cfg.Count > 1 {
		log.Logf(0, "limiting number of VMs from %v to 1 in debug mode", cfg.Count)
		cfg.Count = 1
	}
	if !osutil.IsExist(env.Image) {
		return nil, fmt.Errorf("image file %q does not exist", env.Image)
	}
	pool := &Pool{
		cfg: cfg,
		env: env,
	}
	return pool, nil
}

func (pool *Pool) Count() int {
	return pool.cfg.Count
}

func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
	rootDir := filepath.Clean(filepath.Join(workdir, "..", "gvisor_root"))
	imageDir := filepath.Join(workdir, "image")
	bundleDir := filepath.Join(workdir, "bundle")
	osutil.MkdirAll(rootDir)
	osutil.MkdirAll(bundleDir)
	osutil.MkdirAll(imageDir)

	caps := ""
	for _, c := range sandboxCaps {
		if caps != "" {
			caps += ", "
		}
		caps += "\"" + c + "\""
	}
	vmConfig := fmt.Sprintf(configTempl, imageDir, caps)
	if err := osutil.WriteFile(filepath.Join(bundleDir, "config.json"), []byte(vmConfig)); err != nil {
		return nil, err
	}
	bin, err := exec.LookPath(os.Args[0])
	if err != nil {
		return nil, fmt.Errorf("failed to lookup %v: %v", os.Args[0], err)
	}
	if err := osutil.CopyFile(bin, filepath.Join(imageDir, "init")); err != nil {
		return nil, err
	}

	rpipe, wpipe, err := osutil.LongPipe()
	if err != nil {
		return nil, err
	}
	var tee io.Writer
	if pool.env.Debug {
		tee = os.Stdout
	}
	merger := vmimpl.NewOutputMerger(tee)
	merger.Add("gvisor", rpipe)

	inst := &instance{
		cfg:      pool.cfg,
		image:    pool.env.Image,
		debug:    pool.env.Debug,
		rootDir:  rootDir,
		imageDir: imageDir,
		name:     fmt.Sprintf("%v-%v", pool.env.Name, index),
		merger:   merger,
	}

	// Kill the previous instance in case it's still running.
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	time.Sleep(3 * time.Second)

	cmd := inst.runscCmd("run", "-bundle", bundleDir, inst.name)
	cmd.Stdout = wpipe
	cmd.Stderr = wpipe
	if err := cmd.Start(); err != nil {
		wpipe.Close()
		merger.Wait()
		return nil, err
	}
	inst.cmd = cmd
	wpipe.Close()

	if err := inst.waitBoot(); err != nil {
		inst.Close()
		return nil, err
	}
	return inst, nil
}

func (inst *instance) waitBoot() error {
	errorMsg := []byte("FATAL ERROR:")
	bootedMsg := []byte(initStartMsg)
	timeout := time.NewTimer(time.Minute)
	defer timeout.Stop()
	var output []byte
	for {
		select {
		case out := <-inst.merger.Output:
			output = append(output, out...)
			if pos := bytes.Index(output, errorMsg); pos != -1 {
				end := bytes.IndexByte(output[pos:], '\n')
				if end == -1 {
					end = len(output)
				} else {
					end += pos
				}
				return vmimpl.BootError{
					Title:  string(output[pos:end]),
					Output: output,
				}
			}
			if bytes.Contains(output, bootedMsg) {
				return nil
			}
		case err := <-inst.merger.Err:
			return vmimpl.BootError{
				Title:  fmt.Sprintf("runsc failed: %v", err),
				Output: output,
			}
		case <-timeout.C:
			return vmimpl.BootError{
				Title:  "init process did not start",
				Output: output,
			}
		}
	}
}

func (inst *instance) runscCmd(add ...string) *exec.Cmd {
	args := []string{
		"-root", inst.rootDir,
		"-watchdog-action=panic",
		"-trace-signal=12",
		"-network=none",
		"-debug",
	}
	if inst.cfg.RunscArgs != "" {
		args = append(args, strings.Split(inst.cfg.RunscArgs, " ")...)
	}
	args = append(args, add...)
	cmd := osutil.Command(inst.image, args...)
	cmd.Env = []string{
		"GOTRACEBACK=all",
		"GORACE=halt_on_error=1",
	}
	return cmd
}

func (inst *instance) Close() {
	time.Sleep(3 * time.Second)
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	inst.cmd.Process.Kill()
	inst.merger.Wait()
	inst.cmd.Wait()
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	time.Sleep(3 * time.Second)
}

func (inst *instance) Forward(port int) (string, error) {
	if inst.port != 0 {
		return "", fmt.Errorf("forward port is already setup")
	}
	inst.port = port
	return "stdin", nil
}

func (inst *instance) Copy(hostSrc string) (string, error) {
	fname := filepath.Base(hostSrc)
	if err := osutil.CopyFile(hostSrc, filepath.Join(inst.imageDir, fname)); err != nil {
		return "", err
	}
	if err := os.Chmod(inst.imageDir, 0777); err != nil {
		return "", err
	}
	return filepath.Join("/", fname), nil
}

func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (
	<-chan []byte, <-chan error, error) {
	args := []string{"exec", "-user=0:0"}
	for _, c := range sandboxCaps {
		args = append(args, "-cap", c)
	}
	args = append(args, inst.name)
	args = append(args, strings.Split(command, " ")...)
	cmd := inst.runscCmd(args...)

	rpipe, wpipe, err := osutil.LongPipe()
	if err != nil {
		return nil, nil, err
	}
	defer wpipe.Close()
	inst.merger.Add("cmd", rpipe)
	cmd.Stdout = wpipe
	cmd.Stderr = wpipe

	guestSock, err := inst.guestProxy()
	if err != nil {
		return nil, nil, err
	}
	if guestSock != nil {
		defer guestSock.Close()
		cmd.Stdin = guestSock
	}

	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}
	errc := make(chan error, 1)
	signal := func(err error) {
		select {
		case errc <- err:
		default:
		}
	}

	go func() {
		select {
		case <-time.After(timeout):
			signal(vmimpl.ErrTimeout)
		case <-stop:
			signal(vmimpl.ErrTimeout)
		case err := <-inst.merger.Err:
			cmd.Process.Kill()
			if cmdErr := cmd.Wait(); cmdErr == nil {
				// If the command exited successfully, we got EOF error from merger.
				// But in this case no error has happened and the EOF is expected.
				err = nil
			}
			signal(err)
			return
		}
		cmd.Process.Kill()
		cmd.Wait()
	}()
	return inst.merger.Output, errc, nil
}

func (inst *instance) guestProxy() (*os.File, error) {
	if inst.port == 0 {
		return nil, nil
	}
	// One does not simply let gvisor guest connect to host tcp port.
	// We create a unix socket, pass it to guest in stdin.
	// Guest will use it instead of dialing manager directly.
	// On host we connect to manager tcp port and proxy between the tcp and unix connections.
	socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
	if err != nil {
		return nil, err
	}
	hostSock := os.NewFile(uintptr(socks[0]), "host unix proxy")
	guestSock := os.NewFile(uintptr(socks[1]), "guest unix proxy")
	conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", inst.port))
	if err != nil {
		conn.Close()
		hostSock.Close()
		guestSock.Close()
		return nil, err
	}
	go func() {
		io.Copy(hostSock, conn)
		hostSock.Close()
	}()
	go func() {
		io.Copy(conn, hostSock)
		conn.Close()
	}()
	return guestSock, nil
}

func (inst *instance) Diagnose() bool {
	osutil.Run(time.Minute, inst.runscCmd("debug", "-signal=12", inst.name))
	return true
}

func init() {
	if os.Getenv("SYZ_GVISOR_PROXY") != "" {
		fmt.Fprintf(os.Stderr, initStartMsg)
		select {}
	}
}

const initStartMsg = "SYZKALLER INIT STARTED\n"

const configTempl = `
{
	"root": {
		"path": "%[1]v",
		"readonly": true
	},
	"process":{
                "args": ["/init"],
                "cwd": "/tmp",
                "env": ["SYZ_GVISOR_PROXY=1"],
                "capabilities": {
                	"bounding": [%[2]v],
                	"effective": [%[2]v],
                	"inheritable": [%[2]v],
                	"permitted": [%[2]v],
                	"ambient": [%[2]v]
                }
	}
}
`

var sandboxCaps = []string{
	"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID",
	"CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE",
	"CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW",
	"CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT",
	"CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE",
	"CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE",
	"CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP", "CAP_MAC_OVERRIDE", "CAP_MAC_ADMIN",
	"CAP_SYSLOG", "CAP_WAKE_ALARM", "CAP_BLOCK_SUSPEND", "CAP_AUDIT_READ",
}
<commit_msg>vm/gvisor: don't close conn on error<commit_after>// Copyright 2018 syzkaller project authors. All rights reserved.
// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file.

// Package gvisor provides support for gVisor, user-space kernel, testing.
// See https://github.com/google/gvisor
package gvisor

import (
	"bytes"
	"fmt"
	"io"
	"net"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/google/syzkaller/pkg/config"
	"github.com/google/syzkaller/pkg/log"
	"github.com/google/syzkaller/pkg/osutil"
	"github.com/google/syzkaller/vm/vmimpl"
)

func init() {
	vmimpl.Register("gvisor", ctor, true)
}

type Config struct {
	Count     int    `json:"count"` // number of VMs to use
	RunscArgs string `json:"runsc_args"`
}

type Pool struct {
	env *vmimpl.Env
	cfg *Config
}

type instance struct {
	cfg      *Config
	image    string
	debug    bool
	rootDir  string
	imageDir string
	name     string
	port     int
	cmd      *exec.Cmd
	merger   *vmimpl.OutputMerger
}

func ctor(env *vmimpl.Env) (vmimpl.Pool, error) {
	cfg := &Config{
		Count: 1,
	}
	if err := config.LoadData(env.Config, cfg); err != nil {
		return nil, fmt.Errorf("failed to parse vm config: %v", err)
	}
	if cfg.Count < 1 || cfg.Count > 128 {
		return nil, fmt.Errorf("invalid config param count: %v, want [1, 128]", cfg.Count)
	}
	if env.Debug && cfg.Count > 1 {
		log.Logf(0, "limiting number of VMs from %v to 1 in debug mode", cfg.Count)
		cfg.Count = 1
	}
	if !osutil.IsExist(env.Image) {
		return nil, fmt.Errorf("image file %q does not exist", env.Image)
	}
	pool := &Pool{
		cfg: cfg,
		env: env,
	}
	return pool, nil
}

func (pool *Pool) Count() int {
	return pool.cfg.Count
}

func (pool *Pool) Create(workdir string, index int) (vmimpl.Instance, error) {
	rootDir := filepath.Clean(filepath.Join(workdir, "..", "gvisor_root"))
	imageDir := filepath.Join(workdir, "image")
	bundleDir := filepath.Join(workdir, "bundle")
	osutil.MkdirAll(rootDir)
	osutil.MkdirAll(bundleDir)
	osutil.MkdirAll(imageDir)

	caps := ""
	for _, c := range sandboxCaps {
		if caps != "" {
			caps += ", "
		}
		caps += "\"" + c + "\""
	}
	vmConfig := fmt.Sprintf(configTempl, imageDir, caps)
	if err := osutil.WriteFile(filepath.Join(bundleDir, "config.json"), []byte(vmConfig)); err != nil {
		return nil, err
	}
	bin, err := exec.LookPath(os.Args[0])
	if err != nil {
		return nil, fmt.Errorf("failed to lookup %v: %v", os.Args[0], err)
	}
	if err := osutil.CopyFile(bin, filepath.Join(imageDir, "init")); err != nil {
		return nil, err
	}

	rpipe, wpipe, err := osutil.LongPipe()
	if err != nil {
		return nil, err
	}
	var tee io.Writer
	if pool.env.Debug {
		tee = os.Stdout
	}
	merger := vmimpl.NewOutputMerger(tee)
	merger.Add("gvisor", rpipe)

	inst := &instance{
		cfg:      pool.cfg,
		image:    pool.env.Image,
		debug:    pool.env.Debug,
		rootDir:  rootDir,
		imageDir: imageDir,
		name:     fmt.Sprintf("%v-%v", pool.env.Name, index),
		merger:   merger,
	}

	// Kill the previous instance in case it's still running.
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	time.Sleep(3 * time.Second)

	cmd := inst.runscCmd("run", "-bundle", bundleDir, inst.name)
	cmd.Stdout = wpipe
	cmd.Stderr = wpipe
	if err := cmd.Start(); err != nil {
		wpipe.Close()
		merger.Wait()
		return nil, err
	}
	inst.cmd = cmd
	wpipe.Close()

	if err := inst.waitBoot(); err != nil {
		inst.Close()
		return nil, err
	}
	return inst, nil
}

func (inst *instance) waitBoot() error {
	errorMsg := []byte("FATAL ERROR:")
	bootedMsg := []byte(initStartMsg)
	timeout := time.NewTimer(time.Minute)
	defer timeout.Stop()
	var output []byte
	for {
		select {
		case out := <-inst.merger.Output:
			output = append(output, out...)
			if pos := bytes.Index(output, errorMsg); pos != -1 {
				end := bytes.IndexByte(output[pos:], '\n')
				if end == -1 {
					end = len(output)
				} else {
					end += pos
				}
				return vmimpl.BootError{
					Title:  string(output[pos:end]),
					Output: output,
				}
			}
			if bytes.Contains(output, bootedMsg) {
				return nil
			}
		case err := <-inst.merger.Err:
			return vmimpl.BootError{
				Title:  fmt.Sprintf("runsc failed: %v", err),
				Output: output,
			}
		case <-timeout.C:
			return vmimpl.BootError{
				Title:  "init process did not start",
				Output: output,
			}
		}
	}
}

func (inst *instance) runscCmd(add ...string) *exec.Cmd {
	args := []string{
		"-root", inst.rootDir,
		"-watchdog-action=panic",
		"-trace-signal=12",
		"-network=none",
		"-debug",
	}
	if inst.cfg.RunscArgs != "" {
		args = append(args, strings.Split(inst.cfg.RunscArgs, " ")...)
	}
	args = append(args, add...)
	cmd := osutil.Command(inst.image, args...)
	cmd.Env = []string{
		"GOTRACEBACK=all",
		"GORACE=halt_on_error=1",
	}
	return cmd
}

func (inst *instance) Close() {
	time.Sleep(3 * time.Second)
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	inst.cmd.Process.Kill()
	inst.merger.Wait()
	inst.cmd.Wait()
	osutil.Run(time.Minute, inst.runscCmd("delete", "-force", inst.name))
	time.Sleep(3 * time.Second)
}

func (inst *instance) Forward(port int) (string, error) {
	if inst.port != 0 {
		return "", fmt.Errorf("forward port is already setup")
	}
	inst.port = port
	return "stdin", nil
}

func (inst *instance) Copy(hostSrc string) (string, error) {
	fname := filepath.Base(hostSrc)
	if err := osutil.CopyFile(hostSrc, filepath.Join(inst.imageDir, fname)); err != nil {
		return "", err
	}
	if err := os.Chmod(inst.imageDir, 0777); err != nil {
		return "", err
	}
	return filepath.Join("/", fname), nil
}

func (inst *instance) Run(timeout time.Duration, stop <-chan bool, command string) (
	<-chan []byte, <-chan error, error) {
	args := []string{"exec", "-user=0:0"}
	for _, c := range sandboxCaps {
		args = append(args, "-cap", c)
	}
	args = append(args, inst.name)
	args = append(args, strings.Split(command, " ")...)
	cmd := inst.runscCmd(args...)

	rpipe, wpipe, err := osutil.LongPipe()
	if err != nil {
		return nil, nil, err
	}
	defer wpipe.Close()
	inst.merger.Add("cmd", rpipe)
	cmd.Stdout = wpipe
	cmd.Stderr = wpipe

	guestSock, err := inst.guestProxy()
	if err != nil {
		return nil, nil, err
	}
	if guestSock != nil {
		defer guestSock.Close()
		cmd.Stdin = guestSock
	}

	if err := cmd.Start(); err != nil {
		return nil, nil, err
	}
	errc := make(chan error, 1)
	signal := func(err error) {
		select {
		case errc <- err:
		default:
		}
	}

	go func() {
		select {
		case <-time.After(timeout):
			signal(vmimpl.ErrTimeout)
		case <-stop:
			signal(vmimpl.ErrTimeout)
		case err := <-inst.merger.Err:
			cmd.Process.Kill()
			if cmdErr := cmd.Wait(); cmdErr == nil {
				// If the command exited successfully, we got EOF error from merger.
				// But in this case no error has happened and the EOF is expected.
				err = nil
			}
			signal(err)
			return
		}
		cmd.Process.Kill()
		cmd.Wait()
	}()
	return inst.merger.Output, errc, nil
}

func (inst *instance) guestProxy() (*os.File, error) {
	if inst.port == 0 {
		return nil, nil
	}
	// One does not simply let gvisor guest connect to host tcp port.
	// We create a unix socket, pass it to guest in stdin.
	// Guest will use it instead of dialing manager directly.
	// On host we connect to manager tcp port and proxy between the tcp and unix connections.
	socks, err := syscall.Socketpair(syscall.AF_UNIX, syscall.SOCK_STREAM, 0)
	if err != nil {
		return nil, err
	}
	hostSock := os.NewFile(uintptr(socks[0]), "host unix proxy")
	guestSock := os.NewFile(uintptr(socks[1]), "guest unix proxy")
	conn, err := net.Dial("tcp", fmt.Sprintf("localhost:%v", inst.port))
	if err != nil {
		hostSock.Close()
		guestSock.Close()
		return nil, err
	}
	go func() {
		io.Copy(hostSock, conn)
		hostSock.Close()
	}()
	go func() {
		io.Copy(conn, hostSock)
		conn.Close()
	}()
	return guestSock, nil
}

func (inst *instance) Diagnose() bool {
	osutil.Run(time.Minute, inst.runscCmd("debug", "-signal=12", inst.name))
	return true
}

func init() {
	if os.Getenv("SYZ_GVISOR_PROXY") != "" {
		fmt.Fprintf(os.Stderr, initStartMsg)
		select {}
	}
}

const initStartMsg = "SYZKALLER INIT STARTED\n"

const configTempl = `
{
	"root": {
		"path": "%[1]v",
		"readonly": true
	},
	"process":{
                "args": ["/init"],
                "cwd": "/tmp",
                "env": ["SYZ_GVISOR_PROXY=1"],
                "capabilities": {
                	"bounding": [%[2]v],
                	"effective": [%[2]v],
                	"inheritable": [%[2]v],
                	"permitted": [%[2]v],
                	"ambient": [%[2]v]
                }
	}
}
`

var sandboxCaps = []string{
	"CAP_CHOWN", "CAP_DAC_OVERRIDE", "CAP_DAC_READ_SEARCH", "CAP_FOWNER", "CAP_FSETID",
	"CAP_KILL", "CAP_SETGID", "CAP_SETUID", "CAP_SETPCAP", "CAP_LINUX_IMMUTABLE",
	"CAP_NET_BIND_SERVICE", "CAP_NET_BROADCAST", "CAP_NET_ADMIN", "CAP_NET_RAW",
	"CAP_IPC_LOCK", "CAP_IPC_OWNER", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_CHROOT",
	"CAP_SYS_PTRACE", "CAP_SYS_PACCT", "CAP_SYS_ADMIN", "CAP_SYS_BOOT", "CAP_SYS_NICE",
	"CAP_SYS_RESOURCE", "CAP_SYS_TIME", "CAP_SYS_TTY_CONFIG", "CAP_MKNOD", "CAP_LEASE",
	"CAP_AUDIT_WRITE", "CAP_AUDIT_CONTROL", "CAP_SETFCAP", "CAP_MAC_OVERRIDE", "CAP_MAC_ADMIN",
	"CAP_SYSLOG", "CAP_WAKE_ALARM", "CAP_BLOCK_SUSPEND", "CAP_AUDIT_READ",
}
<|endoftext|>


<commit_before>package gorouter

import (
	"bytes"
	"github.com/julienschmidt/httprouter"
	"net/http"
	"net/http/httptest"
	"testing"
)

//GoRouter benchmark tests functions

func benchmarkGoRouterStaticCall(t int, b *testing.B) {
	var path string
	part := "/x"
	for i := 0; i < t; i++ {
		path += part
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, path, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkGoRouterStaticParallel(t int, b *testing.B) {
	var path string
	part := "/x"
	for i := 0; i < t; i++ {
		path += part
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	req, err := http.NewRequest(GET, path, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		var buf bytes.Buffer
		w := httptest.NewRecorder()
		for pb.Next() {
			buf.Reset()
			s.ServeHTTP(w, req)
		}
	})
}

func benchmarkGoRouterWildcardCall(t int, b *testing.B) {
	var path, rpath string
	part := "/{x}"
	rpart := "/x"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkGoRouterWildcardParallel(t int, b *testing.B) {
	var path, rpath string
	part := "/{x}"
	rpart := "/x"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		var buf bytes.Buffer
		w := httptest.NewRecorder()
		for pb.Next() {
			buf.Reset()
			s.ServeHTTP(w, req)
		}
	})
}

func benchmarkGoRouterRegexpCall(t int, b *testing.B) {
	var path, rpath string
	part := "/{x:r([a-z]+)go}"
	rpart := "/rxgo"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkGoRouterRegexpParallel(t int, b *testing.B) {
	var path, rpath string
	part := "/{x:r([a-z]+)go}"
	rpart := "/rxgo"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		var buf bytes.Buffer
		w := httptest.NewRecorder()
		for pb.Next() {
			buf.Reset()
			s.ServeHTTP(w, req)
		}
	})
}

//HttpRouter benchmark tests functions

func benchmarkHttpRouterStaticCall(t int, b *testing.B) {
	var path string
	part := "/x"
	for i := 0; i < t; i++ {
		path += part
	}

	s := httprouter.New()
	s.GET(path, func(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {})

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, path, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkHttpRouterStaticParallel(t int, b *testing.B) {
	var path string
	part := "/x"
	for i := 0; i < t; i++ {
		path += part
	}

	s := httprouter.New()
	s.GET(path, func(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {})

	req, err := http.NewRequest(GET, path, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		var buf bytes.Buffer
		w := httptest.NewRecorder()
		for pb.Next() {
			buf.Reset()
			s.ServeHTTP(w, req)
		}
	})
}

func benchmarkHttpRouterWildcardCall(t int, b *testing.B) {
	var path, rpath string
	part := "/:x"
	rpart := "/x"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := httprouter.New()
	s.GET(path, func(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {})

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkHttpRouterWildcardParallel(t int, b *testing.B) {
	var path, rpath string
	part := "/:x"
	rpart := "/x"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := httprouter.New()
	s.GET(path, func(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {})

	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		var buf bytes.Buffer
		w := httptest.NewRecorder()
		for pb.Next() {
			buf.Reset()
			s.ServeHTTP(w, req)
		}
	})
}

//GoRouter benchmark tests
func BenchmarkGoRouterStatic1(b *testing.B)  { benchmarkGoRouterStaticCall(1, b) }
func BenchmarkGoRouterStatic2(b *testing.B)  { benchmarkGoRouterStaticCall(2, b) }
func BenchmarkGoRouterStatic3(b *testing.B)  { benchmarkGoRouterStaticCall(3, b) }
func BenchmarkGoRouterStatic5(b *testing.B)  { benchmarkGoRouterStaticCall(5, b) }
func BenchmarkGoRouterStatic10(b *testing.B) { benchmarkGoRouterStaticCall(10, b) }
func BenchmarkGoRouterStatic20(b *testing.B) { benchmarkGoRouterStaticCall(20, b) }

func BenchmarkGoRouterWildcard1(b *testing.B)  { benchmarkGoRouterWildcardCall(1, b) }
func BenchmarkGoRouterWildcard2(b *testing.B)  { benchmarkGoRouterWildcardCall(2, b) }
func BenchmarkGoRouterWildcard3(b *testing.B)  { benchmarkGoRouterWildcardCall(3, b) }
func BenchmarkGoRouterWildcard5(b *testing.B)  { benchmarkGoRouterWildcardCall(5, b) }
func BenchmarkGoRouterWildcard10(b *testing.B) { benchmarkGoRouterWildcardCall(10, b) }
func BenchmarkGoRouterWildcard20(b *testing.B) { benchmarkGoRouterWildcardCall(20, b) }

func BenchmarkGoRouterRegexp1(b *testing.B)  { benchmarkGoRouterRegexpCall(1, b) }
func BenchmarkGoRouterRegexp2(b *testing.B)  { benchmarkGoRouterRegexpCall(2, b) }
func BenchmarkGoRouterRegexp3(b *testing.B)  { benchmarkGoRouterRegexpCall(3, b) }
func BenchmarkGoRouterRegexp5(b *testing.B)  { benchmarkGoRouterRegexpCall(5, b) }
func BenchmarkGoRouterRegexp10(b *testing.B) { benchmarkGoRouterRegexpCall(10, b) }
func BenchmarkGoRouterRegexp20(b *testing.B) { benchmarkGoRouterRegexpCall(20, b) }

func BenchmarkGoRouterStaticParallel1(b *testing.B)  { benchmarkGoRouterStaticParallel(1, b) }
func BenchmarkGoRouterStaticParallel2(b *testing.B)  { benchmarkGoRouterStaticParallel(2, b) }
func BenchmarkGoRouterStaticParallel3(b *testing.B)  { benchmarkGoRouterStaticParallel(3, b) }
func BenchmarkGoRouterStaticParallel5(b *testing.B)  { benchmarkGoRouterStaticParallel(5, b) }
func BenchmarkGoRouterStaticParallel10(b *testing.B) { benchmarkGoRouterStaticParallel(10, b) }
func BenchmarkGoRouterStaticParallel20(b *testing.B) { benchmarkGoRouterStaticParallel(20, b) }

func BenchmarkGoRouterWildcardParallel1(b *testing.B)  { benchmarkGoRouterWildcardParallel(1, b) }
func BenchmarkGoRouterWildcardParallel2(b *testing.B)  { benchmarkGoRouterWildcardParallel(2, b) }
func BenchmarkGoRouterWildcardParallel3(b *testing.B)  { benchmarkGoRouterWildcardParallel(3, b) }
func BenchmarkGoRouterWildcardParallel5(b *testing.B)  { benchmarkGoRouterWildcardParallel(5, b) }
func BenchmarkGoRouterWildcardParallel10(b *testing.B) { benchmarkGoRouterWildcardParallel(10, b) }
func BenchmarkGoRouterWildcardParallel20(b *testing.B) { benchmarkGoRouterWildcardParallel(20, b) }

func BenchmarkGoRouterRegexpParallel1(b *testing.B)  { benchmarkGoRouterRegexpParallel(1, b) }
func BenchmarkGoRouterRegexpParallel2(b *testing.B)  { benchmarkGoRouterRegexpParallel(2, b) }
func BenchmarkGoRouterRegexpParallel3(b *testing.B)  { benchmarkGoRouterRegexpParallel(3, b) }
func BenchmarkGoRouterRegexpParallel5(b *testing.B)  { benchmarkGoRouterRegexpParallel(5, b) }
func BenchmarkGoRouterRegexpParallel10(b *testing.B) { benchmarkGoRouterRegexpParallel(10, b) }
func BenchmarkGoRouterRegexpParallel20(b *testing.B) { benchmarkGoRouterRegexpParallel(20, b) }

//HttpRouter benchmark tests for comparison
func BenchmarkHttpRouterStatic1(b *testing.B)  { benchmarkHttpRouterStaticCall(1, b) }
func BenchmarkHttpRouterStatic2(b *testing.B)  { benchmarkHttpRouterStaticCall(2, b) }
func BenchmarkHttpRouterStatic3(b *testing.B)  { benchmarkHttpRouterStaticCall(3, b) }
func BenchmarkHttpRouterStatic5(b *testing.B)  { benchmarkHttpRouterStaticCall(5, b) }
func BenchmarkHttpRouterStatic10(b *testing.B) { benchmarkHttpRouterStaticCall(10, b) }
func BenchmarkHttpRouterStatic20(b *testing.B) { benchmarkHttpRouterStaticCall(20, b) }

func BenchmarkHttpRouterWildcard1(b *testing.B)  { benchmarkHttpRouterWildcardCall(1, b) }
func BenchmarkHttpRouterWildcard2(b *testing.B)  { benchmarkHttpRouterWildcardCall(2, b) }
func BenchmarkHttpRouterWildcard3(b *testing.B)  { benchmarkHttpRouterWildcardCall(3, b) }
func BenchmarkHttpRouterWildcard5(b *testing.B)  { benchmarkHttpRouterWildcardCall(5, b) }
func BenchmarkHttpRouterWildcard10(b *testing.B) { benchmarkHttpRouterWildcardCall(10, b) }
func BenchmarkHttpRouterWildcard20(b *testing.B) { benchmarkHttpRouterWildcardCall(20, b) }

func BenchmarkHttpRouterStaticParallel1(b *testing.B)  { benchmarkHttpRouterStaticParallel(1, b) }
func BenchmarkHttpRouterStaticParallel2(b *testing.B)  { benchmarkHttpRouterStaticParallel(2, b) }
func BenchmarkHttpRouterStaticParallel3(b *testing.B)  { benchmarkHttpRouterStaticParallel(3, b) }
func BenchmarkHttpRouterStaticParallel5(b *testing.B)  { benchmarkHttpRouterStaticParallel(5, b) }
func BenchmarkHttpRouterStaticParallel10(b *testing.B) { benchmarkHttpRouterStaticParallel(10, b) }
func BenchmarkHttpRouterStaticParallel20(b *testing.B) { benchmarkHttpRouterStaticParallel(20, b) }

func BenchmarkHttpRouterWildcardParallel1(b *testing.B)  { benchmarkHttpRouterWildcardParallel(1, b) }
func BenchmarkHttpRouterWildcardParallel2(b *testing.B)  { benchmarkHttpRouterWildcardParallel(2, b) }
func BenchmarkHttpRouterWildcardParallel3(b *testing.B)  { benchmarkHttpRouterWildcardParallel(3, b) }
func BenchmarkHttpRouterWildcardParallel5(b *testing.B)  { benchmarkHttpRouterWildcardParallel(5, b) }
func BenchmarkHttpRouterWildcardParallel10(b *testing.B) { benchmarkHttpRouterWildcardParallel(10, b) }
func BenchmarkHttpRouterWildcardParallel20(b *testing.B) { benchmarkHttpRouterWildcardParallel(20, b) }
<commit_msg>Update benchmarks<commit_after>package gorouter

import (
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/julienschmidt/httprouter"
)

//GoRouter benchmark tests functions

func benchmarkGoRouterStaticCall(t int, b *testing.B) {
	var path string
	part := "/x"
	for i := 0; i < t; i++ {
		path += part
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, path, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkGoRouterStaticParallel(t int, b *testing.B) {
	var path string
	part := "/x"
	for i := 0; i < t; i++ {
		path += part
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	req, err := http.NewRequest(GET, path, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		w := httptest.NewRecorder()
		for pb.Next() {
			s.ServeHTTP(w, req)
		}
	})
}

func benchmarkGoRouterWildcardCall(t int, b *testing.B) {
	var path, rpath string
	part := "/{x}"
	rpart := "/x"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkGoRouterWildcardParallel(t int, b *testing.B) {
	var path, rpath string
	part := "/{x}"
	rpart := "/x"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		w := httptest.NewRecorder()
		for pb.Next() {
			s.ServeHTTP(w, req)
		}
	})
}

func benchmarkGoRouterRegexpCall(t int, b *testing.B) {
	var path, rpath string
	part := "/{x:r([a-z]+)go}"
	rpart := "/rxgo"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkGoRouterRegexpParallel(t int, b *testing.B) {
	var path, rpath string
	part := "/{x:r([a-z]+)go}"
	rpart := "/rxgo"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := New().(*router)
	s.GET(path, http.HandlerFunc(func(_ http.ResponseWriter, _ *http.Request) {}))

	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		w := httptest.NewRecorder()
		for pb.Next() {
			s.ServeHTTP(w, req)
		}
	})
}

//HttpRouter benchmark tests functions

func benchmarkHttpRouterStaticCall(t int, b *testing.B) {
	var path string
	part := "/x"
	for i := 0; i < t; i++ {
		path += part
	}

	s := httprouter.New()
	s.GET(path, func(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {})

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, path, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkHttpRouterStaticParallel(t int, b *testing.B) {
	var path string
	part := "/x"
	for i := 0; i < t; i++ {
		path += part
	}

	s := httprouter.New()
	s.GET(path, func(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {})

	req, err := http.NewRequest(GET, path, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		w := httptest.NewRecorder()
		for pb.Next() {
			s.ServeHTTP(w, req)
		}
	})
}

func benchmarkHttpRouterWildcardCall(t int, b *testing.B) {
	var path, rpath string
	part := "/:x"
	rpart := "/x"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := httprouter.New()
	s.GET(path, func(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {})

	w := httptest.NewRecorder()
	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		s.ServeHTTP(w, req)
	}
}

func benchmarkHttpRouterWildcardParallel(t int, b *testing.B) {
	var path, rpath string
	part := "/:x"
	rpart := "/x"
	for i := 0; i < t; i++ {
		path += part
		rpath += rpart
	}

	s := httprouter.New()
	s.GET(path, func(_ http.ResponseWriter, _ *http.Request, _ httprouter.Params) {})

	req, err := http.NewRequest(GET, rpath, nil)
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()
	b.RunParallel(func(pb *testing.PB) {
		w := httptest.NewRecorder()
		for pb.Next() {
			s.ServeHTTP(w, req)
		}
	})
}

//GoRouter benchmark tests
func BenchmarkGoRouterStatic1(b *testing.B)  { benchmarkGoRouterStaticCall(1, b) }
func BenchmarkGoRouterStatic2(b *testing.B)  { benchmarkGoRouterStaticCall(2, b) }
func BenchmarkGoRouterStatic3(b *testing.B)  { benchmarkGoRouterStaticCall(3, b) }
func BenchmarkGoRouterStatic5(b *testing.B)  { benchmarkGoRouterStaticCall(5, b) }
func BenchmarkGoRouterStatic10(b *testing.B) { benchmarkGoRouterStaticCall(10, b) }
func BenchmarkGoRouterStatic20(b *testing.B) { benchmarkGoRouterStaticCall(20, b) }

func BenchmarkGoRouterWildcard1(b *testing.B)  { benchmarkGoRouterWildcardCall(1, b) }
func BenchmarkGoRouterWildcard2(b *testing.B)  { benchmarkGoRouterWildcardCall(2, b) }
func BenchmarkGoRouterWildcard3(b *testing.B)  { benchmarkGoRouterWildcardCall(3, b) }
func BenchmarkGoRouterWildcard5(b *testing.B)  { benchmarkGoRouterWildcardCall(5, b) }
func BenchmarkGoRouterWildcard10(b *testing.B) { benchmarkGoRouterWildcardCall(10, b) }
func BenchmarkGoRouterWildcard20(b *testing.B) { benchmarkGoRouterWildcardCall(20, b) }

func BenchmarkGoRouterRegexp1(b *testing.B)  { benchmarkGoRouterRegexpCall(1, b) }
func BenchmarkGoRouterRegexp2(b *testing.B)  { benchmarkGoRouterRegexpCall(2, b) }
func BenchmarkGoRouterRegexp3(b *testing.B)  { benchmarkGoRouterRegexpCall(3, b) }
func BenchmarkGoRouterRegexp5(b *testing.B)  { benchmarkGoRouterRegexpCall(5, b) }
func BenchmarkGoRouterRegexp10(b *testing.B) { benchmarkGoRouterRegexpCall(10, b) }
func BenchmarkGoRouterRegexp20(b *testing.B) { benchmarkGoRouterRegexpCall(20, b) }

func BenchmarkGoRouterStaticParallel1(b *testing.B)  { benchmarkGoRouterStaticParallel(1, b) }
func BenchmarkGoRouterStaticParallel2(b *testing.B)  { benchmarkGoRouterStaticParallel(2, b) }
func BenchmarkGoRouterStaticParallel3(b *testing.B)  { benchmarkGoRouterStaticParallel(3, b) }
func BenchmarkGoRouterStaticParallel5(b *testing.B)  { benchmarkGoRouterStaticParallel(5, b) }
func BenchmarkGoRouterStaticParallel10(b *testing.B) { benchmarkGoRouterStaticParallel(10, b) }
func BenchmarkGoRouterStaticParallel20(b *testing.B) { benchmarkGoRouterStaticParallel(20, b) }

func BenchmarkGoRouterWildcardParallel1(b *testing.B)  { benchmarkGoRouterWildcardParallel(1, b) }
func BenchmarkGoRouterWildcardParallel2(b *testing.B)  { benchmarkGoRouterWildcardParallel(2, b) }
func BenchmarkGoRouterWildcardParallel3(b *testing.B)  { benchmarkGoRouterWildcardParallel(3, b) }
func BenchmarkGoRouterWildcardParallel5(b *testing.B)  { benchmarkGoRouterWildcardParallel(5, b) }
func BenchmarkGoRouterWildcardParallel10(b *testing.B) { benchmarkGoRouterWildcardParallel(10, b) }
func BenchmarkGoRouterWildcardParallel20(b *testing.B) { benchmarkGoRouterWildcardParallel(20, b) }

func BenchmarkGoRouterRegexpParallel1(b *testing.B)  { benchmarkGoRouterRegexpParallel(1, b) }
func BenchmarkGoRouterRegexpParallel2(b *testing.B)  { benchmarkGoRouterRegexpParallel(2, b) }
func BenchmarkGoRouterRegexpParallel3(b *testing.B)  { benchmarkGoRouterRegexpParallel(3, b) }
func BenchmarkGoRouterRegexpParallel5(b *testing.B)  { benchmarkGoRouterRegexpParallel(5, b) }
func BenchmarkGoRouterRegexpParallel10(b *testing.B) { benchmarkGoRouterRegexpParallel(10, b) }
func BenchmarkGoRouterRegexpParallel20(b *testing.B) { benchmarkGoRouterRegexpParallel(20, b) }

//HttpRouter benchmark tests for comparison
func BenchmarkHttpRouterStatic1(b *testing.B)  { benchmarkHttpRouterStaticCall(1, b) }
func BenchmarkHttpRouterStatic2(b *testing.B)  { benchmarkHttpRouterStaticCall(2, b) }
func BenchmarkHttpRouterStatic3(b *testing.B)  { benchmarkHttpRouterStaticCall(3, b) }
func BenchmarkHttpRouterStatic5(b *testing.B)  { benchmarkHttpRouterStaticCall(5, b) }
func BenchmarkHttpRouterStatic10(b *testing.B) { benchmarkHttpRouterStaticCall(10, b) }
func BenchmarkHttpRouterStatic20(b *testing.B) { benchmarkHttpRouterStaticCall(20, b) }

func BenchmarkHttpRouterWildcard1(b *testing.B)  { benchmarkHttpRouterWildcardCall(1, b) }
func BenchmarkHttpRouterWildcard2(b *testing.B)  { benchmarkHttpRouterWildcardCall(2, b) }
func BenchmarkHttpRouterWildcard3(b *testing.B)  { benchmarkHttpRouterWildcardCall(3, b) }
func BenchmarkHttpRouterWildcard5(b *testing.B)  { benchmarkHttpRouterWildcardCall(5, b) }
func BenchmarkHttpRouterWildcard10(b *testing.B) { benchmarkHttpRouterWildcardCall(10, b) }
func BenchmarkHttpRouterWildcard20(b *testing.B) { benchmarkHttpRouterWildcardCall(20, b) }

func BenchmarkHttpRouterStaticParallel1(b *testing.B)  { benchmarkHttpRouterStaticParallel(1, b) }
func BenchmarkHttpRouterStaticParallel2(b *testing.B)  { benchmarkHttpRouterStaticParallel(2, b) }
func BenchmarkHttpRouterStaticParallel3(b *testing.B)  { benchmarkHttpRouterStaticParallel(3, b) }
func BenchmarkHttpRouterStaticParallel5(b *testing.B)  { benchmarkHttpRouterStaticParallel(5, b) }
func BenchmarkHttpRouterStaticParallel10(b *testing.B) { benchmarkHttpRouterStaticParallel(10, b) }
func BenchmarkHttpRouterStaticParallel20(b *testing.B) { benchmarkHttpRouterStaticParallel(20, b) }

func BenchmarkHttpRouterWildcardParallel1(b *testing.B)  { benchmarkHttpRouterWildcardParallel(1, b) }
func BenchmarkHttpRouterWildcardParallel2(b *testing.B)  { benchmarkHttpRouterWildcardParallel(2, b) }
func BenchmarkHttpRouterWildcardParallel3(b *testing.B)  { benchmarkHttpRouterWildcardParallel(3, b) }
func BenchmarkHttpRouterWildcardParallel5(b *testing.B)  { benchmarkHttpRouterWildcardParallel(5, b) }
func BenchmarkHttpRouterWildcardParallel10(b *testing.B) { benchmarkHttpRouterWildcardParallel(10, b) }
func BenchmarkHttpRouterWildcardParallel20(b *testing.B) { benchmarkHttpRouterWildcardParallel(20, b) }
<|endoftext|>


<commit_before>package benchmarkbbs_test

import (
	"fmt"
	"math/rand"
	"os"
	"sync"
	"sync/atomic"
	"time"

	"code.cloudfoundry.org/bbs/models"
	"code.cloudfoundry.org/benchmarkbbs/reporter"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/operationq"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

const (
	RepBulkFetching                   = "RepBulkFetching"
	RepBulkLoop                       = "RepBulkLoop"
	RepClaimActualLRP                 = "RepClaimActualLRP"
	RepStartActualLRP                 = "RepStartActualLRP"
	NsyncBulkerFetching               = "NsyncBulkerFetching"
	ConvergenceGathering              = "ConvergenceGathering"
	FetchActualLRPsAndSchedulingInfos = "FetchActualLRPsAndSchedulingInfos"
)

var bulkCycle = 30 * time.Second
var eventCount int32 = 0
var expectedEventCount int32 = 0

func eventCountRunner(counter *int32) func(signals <-chan os.Signal, ready chan<- struct{}) error {
	return func(signals <-chan os.Signal, ready chan<- struct{}) error {
		eventSource, err := bbsClient.SubscribeToEvents(logger)
		Expect(err).NotTo(HaveOccurred())
		close(ready)

		eventChan := make(chan models.Event)
		go func() {
			for {
				event, err := eventSource.Next()
				if err != nil {
					logger.Error("error-getting-next-event", err)
					return
				}
				if event != nil {
					eventChan <- event
				}
			}
		}()

		for {
			select {
			case <-eventChan:
				atomic.AddInt32(counter, 1)

			case <-signals:
				if eventSource != nil {
					err := eventSource.Close()
					if err != nil {
						logger.Error("failed-closing-event-source", err)
					}
				}
				return nil
			}
		}
	}
}

var BenchmarkTests = func(numReps, numTrials int, localRouteEmitters bool) {
	Describe("main benchmark test", func() {
		var process ifrit.Process

		BeforeEach(func() {
			process = ifrit.Invoke(ifrit.RunFunc(eventCountRunner(&eventCount)))
		})

		AfterEach(func() {
			ginkgomon.Kill(process)
		})

		Measure("data for benchmarks", func(b Benchmarker) {
			wg := sync.WaitGroup{}

			// start nsync
			wg.Add(1)
			go nsyncBulkerLoop(b, &wg, numTrials)

			// start convergence
			wg.Add(1)
			go convergence(b, &wg, numTrials, numReps)

			// we need to make sure we don't run out of ports so limit amount of
			// active http requests to 25000
			semaphore := make(chan struct{}, 25000)

			numRouteEmitters := 1

			if localRouteEmitters {
				numRouteEmitters = numReps
			}

			routeEmitterEventCounts := make(map[string]*int32)

			for i := 0; i < numRouteEmitters; i++ {
				cellID := fmt.Sprintf("cell-%d", i)

				routeEmitterEventCount := new(int32)
				routeEmitterEventCounts[cellID] = routeEmitterEventCount

				// start route-emitter
				wg.Add(1)
				go routeEmitter(b, &wg, localRouteEmitters, cellID, routeEmitterEventCount, semaphore, numTrials)
			}

			queue := operationq.NewSlidingQueue(numTrials)

			totalRan := int32(0)
			totalQueued := int32(0)

			for i := 0; i < numReps; i++ {
				cellID := fmt.Sprintf("cell-%d", i)
				wg.Add(1)

				go repBulker(b, &wg, cellID, numTrials, semaphore, &totalQueued, &totalRan, &expectedEventCount, queue)
			}

			wg.Wait()

			eventTolerance := float64(atomic.LoadInt32(&expectedEventCount)) * config.ErrorTolerance

			Eventually(func() int32 {
				return atomic.LoadInt32(&eventCount)
			}, 2*time.Minute).Should(BeNumerically("~", expectedEventCount, eventTolerance), "events received")

			Eventually(func() int32 {
				return atomic.LoadInt32(&totalRan)
			}, 2*time.Minute).Should(Equal(totalQueued), "should have run the same number of queued LRP operations")

			for _, v := range routeEmitterEventCounts {
				Eventually(func() int32 {
					return atomic.LoadInt32(v)
				}, 2*time.Minute).Should(BeNumerically("~", expectedEventCount, eventTolerance), "events received")
			}
		}, 1)
	})
}

func getSleepDuration(loopCounter int, cycleTime time.Duration) time.Duration {
	sleepDuration := cycleTime
	if loopCounter == 0 {
		numMilli := rand.Intn(int(cycleTime.Nanoseconds() / 1000000))
		sleepDuration = time.Duration(numMilli) * time.Millisecond
	}
	return sleepDuration
}

func nsyncBulkerLoop(b Benchmarker, wg *sync.WaitGroup, numTrials int) {
	defer GinkgoRecover()
	logger.Info("start-nsync-bulker-loop")
	defer logger.Info("finish-nsync-bulker-loop")
	defer wg.Done()

	for i := 0; i < numTrials; i++ {
		sleepDuration := getSleepDuration(i, bulkCycle)
		time.Sleep(sleepDuration)
		b.Time("fetch all desired LRP scheduling info", func() {
			desireds, err := bbsClient.DesiredLRPSchedulingInfos(logger, models.DesiredLRPFilter{})
			Expect(err).NotTo(HaveOccurred())
			Expect(len(desireds)).To(BeNumerically("~", expectedLRPCount, expectedLRPVariation), "Number of DesiredLRPs retrieved in Nsync Bulk Loop")
		}, reporter.ReporterInfo{
			MetricName: NsyncBulkerFetching,
		})
	}
}

func convergence(b Benchmarker, wg *sync.WaitGroup, numTrials, numReps int) {
	defer GinkgoRecover()
	logger.Info("start-lrp-convergence-loop")
	defer logger.Info("finish-lrp-convergence-loop")
	defer wg.Done()

	for i := 0; i < numTrials; i++ {
		sleepDuration := getSleepDuration(i, bulkCycle)
		time.Sleep(sleepDuration)
		cellSet := models.NewCellSet()
		for i := 0; i < numReps; i++ {
			cellID := fmt.Sprintf("cell-%d", i)
			presence := models.NewCellPresence(cellID, "earth", "http://planet-earth", "north", models.CellCapacity{}, nil, nil, nil, nil)
			cellSet.Add(&presence)
		}

		b.Time("BBS' internal gathering of LRPs", func() {
			activeDB.ConvergeLRPs(logger, cellSet)
		}, reporter.ReporterInfo{
			MetricName: ConvergenceGathering,
		})
	}
}

func repBulker(b Benchmarker, wg *sync.WaitGroup, cellID string, numTrials int, semaphore chan struct{}, totalQueued, totalRan, expectedEventCount *int32, queue operationq.Queue) {
	defer GinkgoRecover()
	defer wg.Done()

	var err error

	for j := 0; j < numTrials; j++ {
		sleepDuration := getSleepDuration(j, bulkCycle)
		time.Sleep(sleepDuration)

		b.Time("rep bulk loop", func() {
			defer GinkgoRecover()
			var actuals []*models.ActualLRPGroup
			b.Time("rep bulk fetch", func() {
				semaphore <- struct{}{}
				actuals, err = bbsClient.ActualLRPGroups(logger, models.ActualLRPFilter{CellID: cellID})
				<-semaphore
				Expect(err).NotTo(HaveOccurred())
			}, reporter.ReporterInfo{
				MetricName: RepBulkFetching,
			})

			expectedActualLRPCount, ok := expectedActualLRPCounts[cellID]
			Expect(ok).To(BeTrue())

			expectedActualLRPVariation, ok := expectedActualLRPVariations[cellID]
			Expect(ok).To(BeTrue())

			Expect(len(actuals)).To(BeNumerically("~", expectedActualLRPCount, expectedActualLRPVariation), "Number of ActualLRPs retrieved by cell %s in rep bulk loop", cellID)

			numActuals := len(actuals)
			for k := 0; k < numActuals; k++ {
				actualLRP, _ := actuals[k].Resolve()
				atomic.AddInt32(totalQueued, 1)
				queue.Push(&lrpOperation{actualLRP, config.PercentWrites, b, totalRan, expectedEventCount, semaphore})
			}
		}, reporter.ReporterInfo{
			MetricName: RepBulkLoop,
		})
	}
}

func routeEmitter(b Benchmarker, wg *sync.WaitGroup, localRouteEmitters bool, cellID string, routeEmitterEventCount *int32, semaphore chan struct{}, numTrials int) {
	defer GinkgoRecover()

	lagerData := lager.Data{}
	if localRouteEmitters {
		lagerData = lager.Data{"cell-id": cellID}
	}
	logger := logger.WithData(lagerData)
	logger.Info("start-route-emitter-loop")
	defer logger.Info("finish-route-emitter-loop")

	defer wg.Done()

	ifrit.Invoke(ifrit.RunFunc(eventCountRunner(routeEmitterEventCount)))

	for j := 0; j < numTrials; j++ {
		sleepDuration := getSleepDuration(j, bulkCycle)
		time.Sleep(sleepDuration)
		b.Time("fetch all actualLRPs and schedulingInfos", func() {
			semaphore <- struct{}{}
			actuals, err := bbsClient.ActualLRPGroups(logger, models.ActualLRPFilter{})
			<-semaphore
			Expect(err).NotTo(HaveOccurred())
			Expect(len(actuals)).To(BeNumerically("~", expectedLRPCount, expectedLRPVariation), "Number of ActualLRPs retrieved in router-emitter")

			semaphore <- struct{}{}
			desireds, err := bbsClient.DesiredLRPSchedulingInfos(logger, models.DesiredLRPFilter{})
			<-semaphore
			Expect(err).NotTo(HaveOccurred())
			Expect(len(desireds)).To(BeNumerically("~", expectedLRPCount, expectedLRPVariation), "Number of DesiredLRPs retrieved in route-emitter")

		}, reporter.ReporterInfo{
			MetricName: FetchActualLRPsAndSchedulingInfos,
		})
	}
}

type lrpOperation struct {
	actualLRP        *models.ActualLRP
	percentWrites    float64
	b                Benchmarker
	globalCount      *int32
	globalEventCount *int32
	semaphore        chan struct{}
}

func (lo *lrpOperation) Key() string {
	return lo.actualLRP.ProcessGuid
}

func (lo *lrpOperation) Execute() {
	defer GinkgoRecover()
	defer atomic.AddInt32(lo.globalCount, 1)
	var err error
	randomNum := rand.Float64() * 100.0

	// divided by 2 because the start following the claim cause two writes.
	isClaiming := randomNum < (lo.percentWrites / 2)
	actualLRP := lo.actualLRP

	lo.b.Time("start actual LRP", func() {
		netInfo := models.NewActualLRPNetInfo("1.2.3.4", models.NewPortMapping(61999, 8080))
		lo.semaphore <- struct{}{}
		err = bbsClient.StartActualLRP(logger, &actualLRP.ActualLRPKey, &actualLRP.ActualLRPInstanceKey, &netInfo)
		<-lo.semaphore
		Expect(err).NotTo(HaveOccurred())

		// if the actual lrp was not already started, an event will be generated
		if actualLRP.State != models.ActualLRPStateRunning {
			atomic.AddInt32(lo.globalEventCount, 1)
		}
	}, reporter.ReporterInfo{
		MetricName: RepStartActualLRP,
	})

	if isClaiming {
		lo.b.Time("claim actual LRP", func() {
			index := int(actualLRP.ActualLRPKey.Index)
			lo.semaphore <- struct{}{}
			err = bbsClient.ClaimActualLRP(logger, actualLRP.ActualLRPKey.ProcessGuid, index, &actualLRP.ActualLRPInstanceKey)
			<-lo.semaphore
			Expect(err).NotTo(HaveOccurred())
			atomic.AddInt32(lo.globalEventCount, 1)
		}, reporter.ReporterInfo{
			MetricName: RepClaimActualLRP,
		})
	}
}
<commit_msg>filter actual lrps when running in local route emitter configuration<commit_after>package benchmarkbbs_test

import (
	"fmt"
	"math/rand"
	"os"
	"sync"
	"sync/atomic"
	"time"

	"code.cloudfoundry.org/bbs/models"
	"code.cloudfoundry.org/benchmarkbbs/reporter"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/operationq"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/tedsuo/ifrit"
	"github.com/tedsuo/ifrit/ginkgomon"
)

const (
	RepBulkFetching                   = "RepBulkFetching"
	RepBulkLoop                       = "RepBulkLoop"
	RepClaimActualLRP                 = "RepClaimActualLRP"
	RepStartActualLRP                 = "RepStartActualLRP"
	NsyncBulkerFetching               = "NsyncBulkerFetching"
	ConvergenceGathering              = "ConvergenceGathering"
	FetchActualLRPsAndSchedulingInfos = "FetchActualLRPsAndSchedulingInfos"
)

var bulkCycle = 30 * time.Second
var eventCount int32 = 0
var expectedEventCount int32 = 0

func eventCountRunner(counter *int32) func(signals <-chan os.Signal, ready chan<- struct{}) error {
	return func(signals <-chan os.Signal, ready chan<- struct{}) error {
		eventSource, err := bbsClient.SubscribeToEvents(logger)
		Expect(err).NotTo(HaveOccurred())
		close(ready)

		eventChan := make(chan models.Event)
		go func() {
			for {
				event, err := eventSource.Next()
				if err != nil {
					logger.Error("error-getting-next-event", err)
					return
				}
				if event != nil {
					eventChan <- event
				}
			}
		}()

		for {
			select {
			case <-eventChan:
				atomic.AddInt32(counter, 1)

			case <-signals:
				if eventSource != nil {
					err := eventSource.Close()
					if err != nil {
						logger.Error("failed-closing-event-source", err)
					}
				}
				return nil
			}
		}
	}
}

var BenchmarkTests = func(numReps, numTrials int, localRouteEmitters bool) {
	Describe("main benchmark test", func() {
		var process ifrit.Process

		BeforeEach(func() {
			process = ifrit.Invoke(ifrit.RunFunc(eventCountRunner(&eventCount)))
		})

		AfterEach(func() {
			ginkgomon.Kill(process)
		})

		Measure("data for benchmarks", func(b Benchmarker) {
			wg := sync.WaitGroup{}

			// start nsync
			wg.Add(1)
			go nsyncBulkerLoop(b, &wg, numTrials)

			// start convergence
			wg.Add(1)
			go convergence(b, &wg, numTrials, numReps)

			// we need to make sure we don't run out of ports so limit amount of
			// active http requests to 25000
			semaphore := make(chan struct{}, 25000)

			routeEmitterEventCounts := make(map[string]*int32)
			for i := 0; i < numReps; i++ {
				cellID := ""
				if localRouteEmitters {
					cellID = fmt.Sprintf("cell-%d", i)
				}

				routeEmitterEventCount := new(int32)
				routeEmitterEventCounts[cellID] = routeEmitterEventCount

				// start route-emitter
				wg.Add(1)
				go routeEmitter(b, &wg, localRouteEmitters, cellID, routeEmitterEventCount, semaphore, numTrials)

				if !localRouteEmitters {
					break
				}
			}

			queue := operationq.NewSlidingQueue(numTrials)

			totalRan := int32(0)
			totalQueued := int32(0)

			for i := 0; i < numReps; i++ {
				cellID := fmt.Sprintf("cell-%d", i)
				wg.Add(1)

				go repBulker(b, &wg, cellID, numTrials, semaphore, &totalQueued, &totalRan, &expectedEventCount, queue)
			}

			wg.Wait()

			eventTolerance := float64(atomic.LoadInt32(&expectedEventCount)) * config.ErrorTolerance

			Eventually(func() int32 {
				return atomic.LoadInt32(&eventCount)
			}, 2*time.Minute).Should(BeNumerically("~", expectedEventCount, eventTolerance), "events received")

			Eventually(func() int32 {
				return atomic.LoadInt32(&totalRan)
			}, 2*time.Minute).Should(Equal(totalQueued), "should have run the same number of queued LRP operations")

			for _, v := range routeEmitterEventCounts {
				Eventually(func() int32 {
					return atomic.LoadInt32(v)
				}, 2*time.Minute).Should(BeNumerically("~", expectedEventCount, eventTolerance), "events received")
			}
		}, 1)
	})
}

func getSleepDuration(loopCounter int, cycleTime time.Duration) time.Duration {
	sleepDuration := cycleTime
	if loopCounter == 0 {
		numMilli := rand.Intn(int(cycleTime.Nanoseconds() / 1000000))
		sleepDuration = time.Duration(numMilli) * time.Millisecond
	}
	return sleepDuration
}

func nsyncBulkerLoop(b Benchmarker, wg *sync.WaitGroup, numTrials int) {
	defer GinkgoRecover()
	logger.Info("start-nsync-bulker-loop")
	defer logger.Info("finish-nsync-bulker-loop")
	defer wg.Done()

	for i := 0; i < numTrials; i++ {
		sleepDuration := getSleepDuration(i, bulkCycle)
		time.Sleep(sleepDuration)
		b.Time("fetch all desired LRP scheduling info", func() {
			desireds, err := bbsClient.DesiredLRPSchedulingInfos(logger, models.DesiredLRPFilter{})
			Expect(err).NotTo(HaveOccurred())
			Expect(len(desireds)).To(BeNumerically("~", expectedLRPCount, expectedLRPVariation), "Number of DesiredLRPs retrieved in Nsync Bulk Loop")
		}, reporter.ReporterInfo{
			MetricName: NsyncBulkerFetching,
		})
	}
}

func convergence(b Benchmarker, wg *sync.WaitGroup, numTrials, numReps int) {
	defer GinkgoRecover()
	logger.Info("start-lrp-convergence-loop")
	defer logger.Info("finish-lrp-convergence-loop")
	defer wg.Done()

	for i := 0; i < numTrials; i++ {
		sleepDuration := getSleepDuration(i, bulkCycle)
		time.Sleep(sleepDuration)
		cellSet := models.NewCellSet()
		for i := 0; i < numReps; i++ {
			cellID := fmt.Sprintf("cell-%d", i)
			presence := models.NewCellPresence(cellID, "earth", "http://planet-earth", "north", models.CellCapacity{}, nil, nil, nil, nil)
			cellSet.Add(&presence)
		}

		b.Time("BBS' internal gathering of LRPs", func() {
			activeDB.ConvergeLRPs(logger, cellSet)
		}, reporter.ReporterInfo{
			MetricName: ConvergenceGathering,
		})
	}
}

func repBulker(b Benchmarker, wg *sync.WaitGroup, cellID string, numTrials int, semaphore chan struct{}, totalQueued, totalRan, expectedEventCount *int32, queue operationq.Queue) {
	defer GinkgoRecover()
	defer wg.Done()

	var err error

	for j := 0; j < numTrials; j++ {
		sleepDuration := getSleepDuration(j, bulkCycle)
		time.Sleep(sleepDuration)

		b.Time("rep bulk loop", func() {
			defer GinkgoRecover()
			var actuals []*models.ActualLRPGroup
			b.Time("rep bulk fetch", func() {
				semaphore <- struct{}{}
				actuals, err = bbsClient.ActualLRPGroups(logger, models.ActualLRPFilter{CellID: cellID})
				<-semaphore
				Expect(err).NotTo(HaveOccurred())
			}, reporter.ReporterInfo{
				MetricName: RepBulkFetching,
			})

			expectedActualLRPCount, ok := expectedActualLRPCounts[cellID]
			Expect(ok).To(BeTrue())

			expectedActualLRPVariation, ok := expectedActualLRPVariations[cellID]
			Expect(ok).To(BeTrue())

			Expect(len(actuals)).To(BeNumerically("~", expectedActualLRPCount, expectedActualLRPVariation), "Number of ActualLRPs retrieved by cell %s in rep bulk loop", cellID)

			numActuals := len(actuals)
			for k := 0; k < numActuals; k++ {
				actualLRP, _ := actuals[k].Resolve()
				atomic.AddInt32(totalQueued, 1)
				queue.Push(&lrpOperation{actualLRP, config.PercentWrites, b, totalRan, expectedEventCount, semaphore})
			}
		}, reporter.ReporterInfo{
			MetricName: RepBulkLoop,
		})
	}
}

func routeEmitter(b Benchmarker, wg *sync.WaitGroup, localRouteEmitters bool, cellID string, routeEmitterEventCount *int32, semaphore chan struct{}, numTrials int) {
	defer GinkgoRecover()

	logger := logger.WithData(lager.Data{"cell-id": cellID})

	logger.Info("start-route-emitter-loop")
	defer logger.Info("finish-route-emitter-loop")

	defer wg.Done()

	ifrit.Invoke(ifrit.RunFunc(eventCountRunner(routeEmitterEventCount)))

	var expectedActualLRPCount int
	var expectedActualLRPVariation float64
	if cellID == "" {
		expectedActualLRPCount = expectedLRPCount
		expectedActualLRPVariation = expectedLRPVariation
	} else {
		var ok bool
		expectedActualLRPCount, ok = expectedActualLRPCounts[cellID]
		Expect(ok).To(BeTrue())

		expectedActualLRPVariation, ok = expectedActualLRPVariations[cellID]
		Expect(ok).To(BeTrue())
	}

	for j := 0; j < numTrials; j++ {
		sleepDuration := getSleepDuration(j, bulkCycle)
		time.Sleep(sleepDuration)
		b.Time("fetch all actualLRPs and schedulingInfos", func() {
			semaphore <- struct{}{}
			actuals, err := bbsClient.ActualLRPGroups(logger, models.ActualLRPFilter{CellID: cellID})
			<-semaphore
			Expect(err).NotTo(HaveOccurred())
			Expect(len(actuals)).To(BeNumerically("~", expectedActualLRPCount, expectedActualLRPVariation), "Number of ActualLRPs retrieved in router-emitter")

			semaphore <- struct{}{}
			desireds, err := bbsClient.DesiredLRPSchedulingInfos(logger, models.DesiredLRPFilter{})
			<-semaphore
			Expect(err).NotTo(HaveOccurred())
			Expect(len(desireds)).To(BeNumerically("~", expectedLRPCount, expectedLRPVariation), "Number of DesiredLRPs retrieved in route-emitter")

		}, reporter.ReporterInfo{
			MetricName: FetchActualLRPsAndSchedulingInfos,
		})
	}
}

type lrpOperation struct {
	actualLRP        *models.ActualLRP
	percentWrites    float64
	b                Benchmarker
	globalCount      *int32
	globalEventCount *int32
	semaphore        chan struct{}
}

func (lo *lrpOperation) Key() string {
	return lo.actualLRP.ProcessGuid
}

func (lo *lrpOperation) Execute() {
	defer GinkgoRecover()
	defer atomic.AddInt32(lo.globalCount, 1)
	var err error
	randomNum := rand.Float64() * 100.0

	// divided by 2 because the start following the claim cause two writes.
	isClaiming := randomNum < (lo.percentWrites / 2)
	actualLRP := lo.actualLRP

	lo.b.Time("start actual LRP", func() {
		netInfo := models.NewActualLRPNetInfo("1.2.3.4", models.NewPortMapping(61999, 8080))
		lo.semaphore <- struct{}{}
		err = bbsClient.StartActualLRP(logger, &actualLRP.ActualLRPKey, &actualLRP.ActualLRPInstanceKey, &netInfo)
		<-lo.semaphore
		Expect(err).NotTo(HaveOccurred())

		// if the actual lrp was not already started, an event will be generated
		if actualLRP.State != models.ActualLRPStateRunning {
			atomic.AddInt32(lo.globalEventCount, 1)
		}
	}, reporter.ReporterInfo{
		MetricName: RepStartActualLRP,
	})

	if isClaiming {
		lo.b.Time("claim actual LRP", func() {
			index := int(actualLRP.ActualLRPKey.Index)
			lo.semaphore <- struct{}{}
			err = bbsClient.ClaimActualLRP(logger, actualLRP.ActualLRPKey.ProcessGuid, index, &actualLRP.ActualLRPInstanceKey)
			<-lo.semaphore
			Expect(err).NotTo(HaveOccurred())
			atomic.AddInt32(lo.globalEventCount, 1)
		}, reporter.ReporterInfo{
			MetricName: RepClaimActualLRP,
		})
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"github.com/v2ray/v2ray-core"
	_ "github.com/v2ray/v2ray-core/app/router/rules"
	"github.com/v2ray/v2ray-core/common/log"
	"github.com/v2ray/v2ray-core/shell/point"

	// The following are necessary as they register handlers in their init functions.
	_ "github.com/v2ray/v2ray-core/proxy/blackhole"
	_ "github.com/v2ray/v2ray-core/proxy/dokodemo"
	_ "github.com/v2ray/v2ray-core/proxy/freedom"
	_ "github.com/v2ray/v2ray-core/proxy/http"
	_ "github.com/v2ray/v2ray-core/proxy/shadowsocks"
	_ "github.com/v2ray/v2ray-core/proxy/socks"
	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound"
	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
)

var (
	configFile string
	logLevel   = flag.String("loglevel", "warning", "Level of log info to be printed to console, available value: debug, info, warning, error")
	version    = flag.Bool("version", false, "Show current version of V2Ray.")
	test       = flag.Bool("test", false, "Test config file only, without launching V2Ray server.")
)

func init() {
	defaultConfigFile := ""
	workingDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		defaultConfigFile = filepath.Join(workingDir, "config.json")
	}
	flag.StringVar(&configFile, "config", defaultConfigFile, "Config file for this Point server.")
}

func main() {
	flag.Parse()

	core.PrintVersion()

	if *version {
		return
	}

	switch *logLevel {
	case "debug":
		log.SetLogLevel(log.DebugLevel)
	case "info":
		log.SetLogLevel(log.InfoLevel)
	case "warning":
		log.SetLogLevel(log.WarningLevel)
	case "error":
		log.SetLogLevel(log.ErrorLevel)
	default:
		fmt.Println("Unknown log level: " + *logLevel)
		return
	}

	if len(configFile) == 0 {
		log.Error("Config file is not set.")
		return
	}
	config, err := point.LoadConfig(configFile)
	if err != nil {
		log.Error("Failed to read config file (", configFile, "): ", configFile, err)
		return
	}

	if config.LogConfig != nil && len(config.LogConfig.AccessLog) > 0 {
		log.InitAccessLogger(config.LogConfig.AccessLog)
	}

	vPoint, err := point.NewPoint(config)
	if err != nil {
		log.Error("Failed to create Point server: ", err)
		return
	}

	if *test {
		fmt.Println("Configuration OK.")
		return
	}

	err = vPoint.Start()
	if err != nil {
		log.Error("Error starting Point server: ", err)
		return
	}

	finish := make(chan bool)
	<-finish
}
<commit_msg>explictly invoke GC<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"time"

	"github.com/v2ray/v2ray-core"
	_ "github.com/v2ray/v2ray-core/app/router/rules"
	"github.com/v2ray/v2ray-core/common/log"
	"github.com/v2ray/v2ray-core/shell/point"

	// The following are necessary as they register handlers in their init functions.
	_ "github.com/v2ray/v2ray-core/proxy/blackhole"
	_ "github.com/v2ray/v2ray-core/proxy/dokodemo"
	_ "github.com/v2ray/v2ray-core/proxy/freedom"
	_ "github.com/v2ray/v2ray-core/proxy/http"
	_ "github.com/v2ray/v2ray-core/proxy/shadowsocks"
	_ "github.com/v2ray/v2ray-core/proxy/socks"
	_ "github.com/v2ray/v2ray-core/proxy/vmess/inbound"
	_ "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
)

var (
	configFile string
	logLevel   = flag.String("loglevel", "warning", "Level of log info to be printed to console, available value: debug, info, warning, error")
	version    = flag.Bool("version", false, "Show current version of V2Ray.")
	test       = flag.Bool("test", false, "Test config file only, without launching V2Ray server.")
)

func init() {
	defaultConfigFile := ""
	workingDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		defaultConfigFile = filepath.Join(workingDir, "config.json")
	}
	flag.StringVar(&configFile, "config", defaultConfigFile, "Config file for this Point server.")
}

func main() {
	flag.Parse()

	core.PrintVersion()

	if *version {
		return
	}

	switch *logLevel {
	case "debug":
		log.SetLogLevel(log.DebugLevel)
	case "info":
		log.SetLogLevel(log.InfoLevel)
	case "warning":
		log.SetLogLevel(log.WarningLevel)
	case "error":
		log.SetLogLevel(log.ErrorLevel)
	default:
		fmt.Println("Unknown log level: " + *logLevel)
		return
	}

	if len(configFile) == 0 {
		log.Error("Config file is not set.")
		return
	}
	config, err := point.LoadConfig(configFile)
	if err != nil {
		log.Error("Failed to read config file (", configFile, "): ", configFile, err)
		return
	}

	if config.LogConfig != nil && len(config.LogConfig.AccessLog) > 0 {
		log.InitAccessLogger(config.LogConfig.AccessLog)
	}

	vPoint, err := point.NewPoint(config)
	if err != nil {
		log.Error("Failed to create Point server: ", err)
		return
	}

	if *test {
		fmt.Println("Configuration OK.")
		return
	}

	err = vPoint.Start()
	if err != nil {
		log.Error("Error starting Point server: ", err)
		return
	}

	for range time.Tick(time.Minute) {
		runtime.GC()
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mysqlctl

import (
	"bytes"
	"fmt"

	"vitess.io/vitess/go/mysql"
	"vitess.io/vitess/go/sqltypes"
	"vitess.io/vitess/go/vt/log"
)

// Note that definitions of local_metadata and shard_metadata should be the same
// as in testing which is defined in config/init_db.sql.
const (
	sqlCreateLocalMetadataTable = `CREATE TABLE IF NOT EXISTS _vt.local_metadata (
  name VARCHAR(255) NOT NULL,
  value VARCHAR(255) NOT NULL,
  PRIMARY KEY (name)
  ) ENGINE=InnoDB`
	sqlCreateShardMetadataTable = `CREATE TABLE IF NOT EXISTS _vt.shard_metadata (
  name VARCHAR(255) NOT NULL,
  value MEDIUMBLOB NOT NULL,
  PRIMARY KEY (name)
  ) ENGINE=InnoDB`
	sqlUpdateLocalMetadataTable = "UPDATE _vt.local_metadata SET db_name='%s' WHERE db_name=''"
	sqlUpdateShardMetadataTable = "UPDATE _vt.shard_metadata SET db_name='%s' WHERE db_name=''"
)

var (
	sqlAlterLocalMetadataTable = []string{
		`ALTER TABLE _vt.local_metadata ADD COLUMN db_name VARBINARY(255) NOT NULL`,
		`ALTER TABLE _vt.local_metadata DROP PRIMARY KEY, ADD PRIMARY KEY(name, db_name)`,
	}
	sqlAlterShardMetadataTable = []string{
		`ALTER TABLE _vt.shard_metadata ADD COLUMN db_name VARBINARY(255) NOT NULL`,
		`ALTER TABLE _vt.shard_metadata DROP PRIMARY KEY, ADD PRIMARY KEY(name, db_name)`,
	}
)

// PopulateMetadataTables creates and fills the _vt.local_metadata table and
// creates _vt.shard_metadata table. _vt.local_metadata table is
// a per-tablet table that is never replicated. This allows queries
// against local_metadata to return different values on different tablets,
// which is used for communicating between Vitess and MySQL-level tools like
// Orchestrator (http://github.com/github/orchestrator).
// _vt.shard_metadata is a replicated table with per-shard information, but it's
// created here to make it easier to create it on databases that were running
// old version of Vitess, or databases that are getting converted to run under
// Vitess.
func PopulateMetadataTables(mysqld MysqlDaemon, localMetadata map[string]string, dbName string) error {
	log.Infof("Populating _vt.local_metadata table...")

	// Get a non-pooled DBA connection.
	conn, err := mysqld.GetDbaConnection()
	if err != nil {
		return err
	}
	defer conn.Close()

	// Disable replication on this session. We close the connection after using
	// it, so there's no need to re-enable replication when we're done.
	if _, err := conn.ExecuteFetch("SET @@session.sql_log_bin = 0", 0, false); err != nil {
		return err
	}

	// Create the database and table if necessary.
	if _, err := conn.ExecuteFetch("CREATE DATABASE IF NOT EXISTS _vt", 0, false); err != nil {
		return err
	}
	if _, err := conn.ExecuteFetch(sqlCreateLocalMetadataTable, 0, false); err != nil {
		return err
	}
	for _, sql := range sqlAlterLocalMetadataTable {
		if _, err := conn.ExecuteFetch(sql, 0, false); err != nil {
			if merr, ok := err.(*mysql.SQLError); ok && merr.Num == mysql.ERDupFieldName {
				log.Errorf("Expected error executing %v: %v", sql, err)
			} else {
				log.Errorf("Unexpected error executing %v: %v", sql, err)
				return err
			}
		}
	}
	if _, err := conn.ExecuteFetch(fmt.Sprintf(sqlUpdateLocalMetadataTable, dbName), 0, false); err != nil {
		return err
	}
	if _, err := conn.ExecuteFetch(sqlCreateShardMetadataTable, 0, false); err != nil {
		return err
	}
	for _, sql := range sqlAlterShardMetadataTable {
		if _, err := conn.ExecuteFetch(sql, 0, false); err != nil {
			if merr, ok := err.(*mysql.SQLError); ok && merr.Num == mysql.ERDupFieldName {
				log.Errorf("Expected error executing %v: %v", sql, err)
			} else {
				log.Errorf("Unexpected error executing %v: %v", sql, err)
				return err
			}
		}
	}
	if _, err := conn.ExecuteFetch(fmt.Sprintf(sqlUpdateShardMetadataTable, dbName), 0, false); err != nil {
		return err
	}

	// Populate local_metadata from the passed list of values.
	if _, err := conn.ExecuteFetch("BEGIN", 0, false); err != nil {
		return err
	}
	for name, val := range localMetadata {
		nameValue := sqltypes.NewVarChar(name)
		valValue := sqltypes.NewVarChar(val)
		dbNameValue := sqltypes.NewVarBinary(dbName)

		queryBuf := bytes.Buffer{}
		queryBuf.WriteString("INSERT INTO _vt.local_metadata (name,value, db_name) VALUES (")
		nameValue.EncodeSQL(&queryBuf)
		queryBuf.WriteByte(',')
		valValue.EncodeSQL(&queryBuf)
		queryBuf.WriteByte(',')
		dbNameValue.EncodeSQL(&queryBuf)
		queryBuf.WriteString(") ON DUPLICATE KEY UPDATE value = ")
		valValue.EncodeSQL(&queryBuf)

		if _, err := conn.ExecuteFetch(queryBuf.String(), 0, false); err != nil {
			return err
		}
	}
	_, err = conn.ExecuteFetch("COMMIT", 0, false)
	return err
}
<commit_msg>default value for db_name to allow rolling deployment of multi-schema changes<commit_after>/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package mysqlctl

import (
	"bytes"
	"fmt"

	"vitess.io/vitess/go/mysql"
	"vitess.io/vitess/go/sqltypes"
	"vitess.io/vitess/go/vt/log"
)

// Note that definitions of local_metadata and shard_metadata should be the same
// as in testing which is defined in config/init_db.sql.
const (
	sqlCreateLocalMetadataTable = `CREATE TABLE IF NOT EXISTS _vt.local_metadata (
  name VARCHAR(255) NOT NULL,
  value VARCHAR(255) NOT NULL,
  PRIMARY KEY (name)
  ) ENGINE=InnoDB`
	sqlCreateShardMetadataTable = `CREATE TABLE IF NOT EXISTS _vt.shard_metadata (
  name VARCHAR(255) NOT NULL,
  value MEDIUMBLOB NOT NULL,
  PRIMARY KEY (name)
  ) ENGINE=InnoDB`
	sqlUpdateLocalMetadataTable = "UPDATE _vt.local_metadata SET db_name='%s' WHERE db_name=''"
	sqlUpdateShardMetadataTable = "UPDATE _vt.shard_metadata SET db_name='%s' WHERE db_name=''"
)

var (
	sqlAlterLocalMetadataTable = []string{
		`ALTER TABLE _vt.local_metadata ADD COLUMN db_name VARBINARY(255) NOT NULL DEFAULT ''`,
		`ALTER TABLE _vt.local_metadata DROP PRIMARY KEY, ADD PRIMARY KEY(name, db_name)`,
	}
	sqlAlterShardMetadataTable = []string{
		`ALTER TABLE _vt.shard_metadata ADD COLUMN db_name VARBINARY(255) NOT NULL DEFAULT ''`,
		`ALTER TABLE _vt.shard_metadata DROP PRIMARY KEY, ADD PRIMARY KEY(name, db_name)`,
	}
)

// PopulateMetadataTables creates and fills the _vt.local_metadata table and
// creates _vt.shard_metadata table. _vt.local_metadata table is
// a per-tablet table that is never replicated. This allows queries
// against local_metadata to return different values on different tablets,
// which is used for communicating between Vitess and MySQL-level tools like
// Orchestrator (http://github.com/github/orchestrator).
// _vt.shard_metadata is a replicated table with per-shard information, but it's
// created here to make it easier to create it on databases that were running
// old version of Vitess, or databases that are getting converted to run under
// Vitess.
func PopulateMetadataTables(mysqld MysqlDaemon, localMetadata map[string]string, dbName string) error {
	log.Infof("Populating _vt.local_metadata table...")

	// Get a non-pooled DBA connection.
	conn, err := mysqld.GetDbaConnection()
	if err != nil {
		return err
	}
	defer conn.Close()

	// Disable replication on this session. We close the connection after using
	// it, so there's no need to re-enable replication when we're done.
	if _, err := conn.ExecuteFetch("SET @@session.sql_log_bin = 0", 0, false); err != nil {
		return err
	}

	// Create the database and table if necessary.
	if _, err := conn.ExecuteFetch("CREATE DATABASE IF NOT EXISTS _vt", 0, false); err != nil {
		return err
	}
	if _, err := conn.ExecuteFetch(sqlCreateLocalMetadataTable, 0, false); err != nil {
		return err
	}
	for _, sql := range sqlAlterLocalMetadataTable {
		if _, err := conn.ExecuteFetch(sql, 0, false); err != nil {
			if merr, ok := err.(*mysql.SQLError); ok && merr.Num == mysql.ERDupFieldName {
				log.Errorf("Expected error executing %v: %v", sql, err)
			} else {
				log.Errorf("Unexpected error executing %v: %v", sql, err)
				return err
			}
		}
	}
	if _, err := conn.ExecuteFetch(fmt.Sprintf(sqlUpdateLocalMetadataTable, dbName), 0, false); err != nil {
		return err
	}
	if _, err := conn.ExecuteFetch(sqlCreateShardMetadataTable, 0, false); err != nil {
		return err
	}
	for _, sql := range sqlAlterShardMetadataTable {
		if _, err := conn.ExecuteFetch(sql, 0, false); err != nil {
			if merr, ok := err.(*mysql.SQLError); ok && merr.Num == mysql.ERDupFieldName {
				log.Errorf("Expected error executing %v: %v", sql, err)
			} else {
				log.Errorf("Unexpected error executing %v: %v", sql, err)
				return err
			}
		}
	}
	if _, err := conn.ExecuteFetch(fmt.Sprintf(sqlUpdateShardMetadataTable, dbName), 0, false); err != nil {
		return err
	}

	// Populate local_metadata from the passed list of values.
	if _, err := conn.ExecuteFetch("BEGIN", 0, false); err != nil {
		return err
	}
	for name, val := range localMetadata {
		nameValue := sqltypes.NewVarChar(name)
		valValue := sqltypes.NewVarChar(val)
		dbNameValue := sqltypes.NewVarBinary(dbName)

		queryBuf := bytes.Buffer{}
		queryBuf.WriteString("INSERT INTO _vt.local_metadata (name,value, db_name) VALUES (")
		nameValue.EncodeSQL(&queryBuf)
		queryBuf.WriteByte(',')
		valValue.EncodeSQL(&queryBuf)
		queryBuf.WriteByte(',')
		dbNameValue.EncodeSQL(&queryBuf)
		queryBuf.WriteString(") ON DUPLICATE KEY UPDATE value = ")
		valValue.EncodeSQL(&queryBuf)

		if _, err := conn.ExecuteFetch(queryBuf.String(), 0, false); err != nil {
			return err
		}
	}
	_, err = conn.ExecuteFetch("COMMIT", 0, false)
	return err
}
<|endoftext|>


<commit_before>/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreedto in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package consultopo

import (
	"path"

	"golang.org/x/net/context"

	"github.com/hashicorp/consul/api"
	"vitess.io/vitess/go/vt/log"
	"vitess.io/vitess/go/vt/topo"
)

// NewMasterParticipation is part of the topo.Server interface
func (s *Server) NewMasterParticipation(name, id string) (topo.MasterParticipation, error) {
	return &consulMasterParticipation{
		s:    s,
		name: name,
		id:   id,
		stop: make(chan struct{}),
		done: make(chan struct{}),
	}, nil
}

// consulMasterParticipation implements topo.MasterParticipation.
//
// We use a key with name <global>/elections/<name> for the lock,
// that contains the id.
type consulMasterParticipation struct {
	// s is our parent consul topo Server
	s *Server

	// name is the name of this MasterParticipation
	name string

	// id is the process's current id.
	id string

	// stop is a channel closed when Stop is called.
	stop chan struct{}

	// done is a channel closed when we're done processing the Stop
	done chan struct{}
}

// WaitForMastership is part of the topo.MasterParticipation interface.
func (mp *consulMasterParticipation) WaitForMastership() (context.Context, error) {

	electionPath := path.Join(mp.s.root, electionsPath, mp.name)
	l, err := mp.s.client.LockOpts(&api.LockOptions{
		Key:   electionPath,
		Value: []byte(mp.id),
	})
	if err != nil {
		return nil, err
	}

	// If Stop was already called, mp.done is closed, so we are interrupted.
	select {
	case <-mp.done:
		return nil, topo.NewError(topo.Interrupted, "mastership")
	default:
	}

	// Try to lock until mp.stop is closed.
	lost, err := l.Lock(mp.stop)
	if err != nil {
		// We can't lock. See if it was because we got canceled.
		select {
		case <-mp.stop:
			close(mp.done)
		default:
		}
		return nil, err
	}

	// We have the lock, keep mastership until we loose it.
	lockCtx, lockCancel := context.WithCancel(context.Background())
	go func() {
		select {
		case <-lost:
			lockCancel()
			// We could have lost the lock. Per consul API, explicitly call Unlock to make sure that session will not be renewed.
			if err := l.Unlock(); err != nil {
				log.Errorf("master election(%v) Unlock failed: %v", mp.name, err)
			}
		case <-mp.stop:
			// Stop was called. We stop the context first,
			// so the running process is not thinking it
			// is the master any more, then we unlock.
			lockCancel()
			if err := l.Unlock(); err != nil {
				log.Errorf("master election(%v) Unlock failed: %v", mp.name, err)
			}
			close(mp.done)
		}
	}()

	return lockCtx, nil
}

// Stop is part of the topo.MasterParticipation interface
func (mp *consulMasterParticipation) Stop() {
	close(mp.stop)
	<-mp.done
}

// GetCurrentMasterID is part of the topo.MasterParticipation interface
func (mp *consulMasterParticipation) GetCurrentMasterID(ctx context.Context) (string, error) {
	electionPath := path.Join(mp.s.root, electionsPath, mp.name)
	pair, _, err := mp.s.kv.Get(electionPath, nil)
	if err != nil {
		return "", err
	}
	if pair == nil {
		return "", nil
	}
	return string(pair.Value), nil
}
<commit_msg>Fix typo in comment<commit_after>/*
Copyright 2017 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreedto in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package consultopo

import (
	"path"

	"golang.org/x/net/context"

	"github.com/hashicorp/consul/api"
	"vitess.io/vitess/go/vt/log"
	"vitess.io/vitess/go/vt/topo"
)

// NewMasterParticipation is part of the topo.Server interface
func (s *Server) NewMasterParticipation(name, id string) (topo.MasterParticipation, error) {
	return &consulMasterParticipation{
		s:    s,
		name: name,
		id:   id,
		stop: make(chan struct{}),
		done: make(chan struct{}),
	}, nil
}

// consulMasterParticipation implements topo.MasterParticipation.
//
// We use a key with name <global>/elections/<name> for the lock,
// that contains the id.
type consulMasterParticipation struct {
	// s is our parent consul topo Server
	s *Server

	// name is the name of this MasterParticipation
	name string

	// id is the process's current id.
	id string

	// stop is a channel closed when Stop is called.
	stop chan struct{}

	// done is a channel closed when we're done processing the Stop
	done chan struct{}
}

// WaitForMastership is part of the topo.MasterParticipation interface.
func (mp *consulMasterParticipation) WaitForMastership() (context.Context, error) {

	electionPath := path.Join(mp.s.root, electionsPath, mp.name)
	l, err := mp.s.client.LockOpts(&api.LockOptions{
		Key:   electionPath,
		Value: []byte(mp.id),
	})
	if err != nil {
		return nil, err
	}

	// If Stop was already called, mp.done is closed, so we are interrupted.
	select {
	case <-mp.done:
		return nil, topo.NewError(topo.Interrupted, "mastership")
	default:
	}

	// Try to lock until mp.stop is closed.
	lost, err := l.Lock(mp.stop)
	if err != nil {
		// We can't lock. See if it was because we got canceled.
		select {
		case <-mp.stop:
			close(mp.done)
		default:
		}
		return nil, err
	}

	// We have the lock, keep mastership until we lose it.
	lockCtx, lockCancel := context.WithCancel(context.Background())
	go func() {
		select {
		case <-lost:
			lockCancel()
			// We could have lost the lock. Per consul API, explicitly call Unlock to make sure that session will not be renewed.
			if err := l.Unlock(); err != nil {
				log.Errorf("master election(%v) Unlock failed: %v", mp.name, err)
			}
		case <-mp.stop:
			// Stop was called. We stop the context first,
			// so the running process is not thinking it
			// is the master any more, then we unlock.
			lockCancel()
			if err := l.Unlock(); err != nil {
				log.Errorf("master election(%v) Unlock failed: %v", mp.name, err)
			}
			close(mp.done)
		}
	}()

	return lockCtx, nil
}

// Stop is part of the topo.MasterParticipation interface
func (mp *consulMasterParticipation) Stop() {
	close(mp.stop)
	<-mp.done
}

// GetCurrentMasterID is part of the topo.MasterParticipation interface
func (mp *consulMasterParticipation) GetCurrentMasterID(ctx context.Context) (string, error) {
	electionPath := path.Join(mp.s.root, electionsPath, mp.name)
	pair, _, err := mp.s.kv.Get(electionPath, nil)
	if err != nil {
		return "", err
	}
	if pair == nil {
		return "", nil
	}
	return string(pair.Value), nil
}
<|endoftext|>


<commit_before>package models

import (
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/mgo.v2/bson"
)

// Effort struct handles the MongoDB schema for each users challenge effort
type Effort struct {
	ID               int     `json:"id"`
	Name             string  `json:"name"`
	Photo            string  `json:"photo"`
	Time             int     `json:"time"`
	Completed        bool    `json:"completed"`
	AverageCadence   float32 `json:"averageCadence"`
	AverageWatts     float32 `json:"averageWatts"`
	AverageHeartRate float32 `json:"averageHeartRate"`
	MaxHeartRate     int     `json:"maxHeartRate"`
}

// Challenge struct handles the MongoDB schema for a challenge
type Challenge struct {
	ID         bson.ObjectId `bson:"_id,omitempty" json:"id"`
	Segment    Segment       `bson:"segment" json:"segment"`
	Challenger Effort        `bson:"challenger" json:"challenger"`
	Challengee Effort        `bson:"challengee" json:"challengee"`
	Status     string        `bson:"status" json:"status"`
	Created    time.Time     `bson:"created" json:"created"`
	Expires    time.Time     `bson:"expires" json:"expires"`
	Completed  time.Time     `bson:"completed" json:"completed"`
	Expired    bool          `bson:"expired" json:"expired"`
	WinnerID   int           `bson:"winnerId" json:"winnerId"`
	WinnerName string        `bson:"winnerName" json:"winnerName"`
	LoserID    int           `bson:"loserId" json:"loserId"`
	LoserName  string        `bson:"loserName" json:"loserName"`
	CreatedAt  time.Time     `bson:"createdAt" json:"createdAt,omitempty"`
	UpdatedAt  time.Time     `bson:"updatedAt" json:"updatedAt,omitempty"`
	DeletedAt  *time.Time    `bson:"deletedAt" json:"deletedAt,omitempty"`
}

// GetChallengeByID gets a single stored challenge from MongoDB
func GetChallengeByID(id bson.ObjectId) (*Challenge, error) {
	var c Challenge

	if err := session.DB(name).C("challenges").Find(bson.M{"_id": id}).One(&c); err != nil {
		log.WithField("ID", id).Error("Unable to find challenge with id in database")
		return nil, err
	}

	return &c, nil
}

// CreateChallenge creates a new challenge in MongoDB
func CreateChallenge(c Challenge) error {
	if err := session.DB(name).C("challenges").Insert(c); err != nil {
		log.Errorf("Unable to create a new challenge:\n %v", err)
		return err
	}
	log.Printf("Challenge successfully created")
	return nil
}

// RemoveChallenge removes a challenge from MongoDB
func RemoveChallenge(id bson.ObjectId) error {
	if err := session.DB(name).C("challenges").Remove(bson.M{"_id": id}); err != nil {
		log.WithField("ID", id).Error("Unable to find challenge with id in database")
		return err
	}
	log.Printf("Challenge successfully removed: %v", id)
	return nil
}
<commit_msg>modify challenges model<commit_after>package models

import (
	"time"

	log "github.com/Sirupsen/logrus"
	"gopkg.in/mgo.v2/bson"
)

// Effort struct handles the MongoDB schema for each users challenge effort
type Effort struct {
	ID               int     `json:"id"`
	Name             string  `json:"name"`
	Photo            string  `json:"photo"`
	Time             int     `json:"time"`
	Completed        bool    `json:"completed"`
	AverageCadence   float32 `json:"averageCadence"`
	AverageWatts     float32 `json:"averageWatts"`
	AverageHeartRate float32 `json:"averageHeartRate"`
	MaxHeartRate     int     `json:"maxHeartRate"`
}

// Challenge struct handles the MongoDB schema for a challenge
type Challenge struct {
	ID         bson.ObjectId `bson:"_id,omitempty" json:"id"`
	Segment    Segment       `bson:"segment" json:"segment"`
	Challenger Effort        `bson:"challenger" json:"challenger"`
	Challengee Effort        `bson:"challengee" json:"challengee"`
	Status     string        `bson:"status" json:"status"`
	Created    time.Time     `bson:"created" json:"created"`
	Expires    time.Time     `bson:"expires" json:"expires"`
	Completed  time.Time     `bson:"completed" json:"completed"`
	Expired    bool          `bson:"expired" json:"expired"`
	WinnerID   int           `bson:"winnerId" json:"winnerId"`
	WinnerName string        `bson:"winnerName" json:"winnerName"`
	LoserID    int           `bson:"loserId" json:"loserId"`
	LoserName  string        `bson:"loserName" json:"loserName"`
	CreatedAt  time.Time     `bson:"createdAt" json:"createdAt,omitempty"`
	UpdatedAt  time.Time     `bson:"updatedAt" json:"updatedAt,omitempty"`
	DeletedAt  *time.Time    `bson:"deletedAt" json:"deletedAt,omitempty"`
}

// GetChallengeByID gets a single stored challenge from MongoDB
func GetChallengeByID(id bson.ObjectId) (*Challenge, error) {
	var c Challenge

	if err := session.DB(name).C("challenges").Find(bson.M{"_id": id}).One(&c); err != nil {
		log.WithField("ID", id).Error("Unable to find challenge with id in database")
		return nil, err
	}

	return &c, nil
}

// CreateChallenge creates a new challenge in MongoDB
func CreateChallenge(c Challenge) error {
	if err := session.DB(name).C("challenges").Insert(c); err != nil {
		log.Errorf("Unable to create a new challenge:\n %v", err)
		return err
	}
	log.Printf("Challenge successfully created")
	return nil
}

// RemoveChallenge removes a challenge from MongoDB
func RemoveChallenge(id bson.ObjectId) error {
	if err := session.DB(name).C("challenges").RemoveId(id); err != nil {
		log.WithField("ID", id).Error("Unable to find challenge with id in database")
		return err
	}
	log.Printf("Challenge successfully removed: %v", id)
	return nil
}
<|endoftext|>


<commit_before>package command

import (
	"os"
	"fmt"
	"time"
	"errors"

	"github.com/dnaeon/gru/minion"

	"code.google.com/p/go-uuid/uuid"
	"github.com/codegangsta/cli"
)

func NewMinionCommands() cli.Command {
	cmd := cli.Command{
		Name: "minion",
		Usage: "manage minions",
		Subcommands: []cli.Command{
			{
				Name: "list",
				Usage: "list registered minions",
				Action: minionListCommand,
			},
			{
				Name: "info",
				Usage: "get info about a minion",
				Action: minionInfoCommand,
			},
			{
				Name: "serve",
				Usage: "start a minion",
				Action: minionServeCommand,
			},
		},
	}

	return cmd
}

// Executes the "minion list" command
func minionListCommand(c *cli.Context) {
	client := newEtcdMinionClientFromFlags(c)
	minions, err := client.MinionList()

	if err != nil {
		displayError(err, 1)
	}

	for _, minion := range minions {
		fmt.Println(minion)
	}
}

// The "minion info" command
func minionInfoCommand(c *cli.Context) {
	if len(c.Args()) == 0 {
		displayError(errors.New("Must provide a minion uuid"), 64)
	}

	arg := c.Args()[0]
	minion := uuid.Parse(arg)
	if minion == nil {
		displayError(errors.New("Bad minion uuid given"), 64)
	}

	client := newEtcdMinionClientFromFlags(c)
	name, err := client.MinionName(minion)
	if err != nil {
		displayError(err, 1)
	}

	lastseen, err := client.MinionLastseen(minion)
	if err != nil {
		displayError(err, 1)
	}

	taskQueue, err := client.MinionTaskQueue(minion)
	if err != nil {
		displayError(err, 1)
	}

	taskLog, err := client.MinionTaskLog(minion)
	if err != nil {
		displayError(err, 1)
	}

	classifierKeys, err := client.MinionClassifierKeys(minion)
	if err != nil {
		displayError(err, 1)
	}

	fmt.Printf("%-15s: %s\n", "Minion", minion)
	fmt.Printf("%-15s: %s\n", "Name", name)
	fmt.Printf("%-15s: %s\n", "Lastseen", time.Unix(lastseen, 0))
	fmt.Printf("%-15s: %d task(s)\n", "Queue", len(taskQueue))
	fmt.Printf("%-15s: %d task(s)\n", "Processed", len(taskLog))
	fmt.Printf("%-15s: %d key(s)\n", "Classifier", len(classifierKeys))
}

// Executes the "minion serve" command
func minionServeCommand(c *cli.Context) {
	hostname, err := os.Hostname()
	if err != nil {
		displayError(err, 1)
	}

	cfg := etcdConfigFromFlags(c)
	m := minion.NewEtcdMinion(hostname, cfg)
	m.Serve()
}
<commit_msg>gructl: ignore errors about missing queue, log and classifier directory<commit_after>package command

import (
	"os"
	"fmt"
	"time"
	"errors"

	"github.com/dnaeon/gru/minion"

	"code.google.com/p/go-uuid/uuid"
	"github.com/codegangsta/cli"

	etcdclient "github.com/coreos/etcd/client"
)

func NewMinionCommands() cli.Command {
	cmd := cli.Command{
		Name: "minion",
		Usage: "manage minions",
		Subcommands: []cli.Command{
			{
				Name: "list",
				Usage: "list registered minions",
				Action: minionListCommand,
			},
			{
				Name: "info",
				Usage: "get info about a minion",
				Action: minionInfoCommand,
			},
			{
				Name: "serve",
				Usage: "start a minion",
				Action: minionServeCommand,
			},
		},
	}

	return cmd
}

// Executes the "minion list" command
func minionListCommand(c *cli.Context) {
	client := newEtcdMinionClientFromFlags(c)
	minions, err := client.MinionList()

	if err != nil {
		displayError(err, 1)
	}

	for _, minion := range minions {
		fmt.Println(minion)
	}
}

// The "minion info" command
func minionInfoCommand(c *cli.Context) {
	if len(c.Args()) == 0 {
		displayError(errors.New("Must provide a minion uuid"), 64)
	}

	arg := c.Args()[0]
	minion := uuid.Parse(arg)
	if minion == nil {
		displayError(errors.New("Bad minion uuid given"), 64)
	}

	client := newEtcdMinionClientFromFlags(c)
	name, err := client.MinionName(minion)
	if err != nil {
		displayError(err, 1)
	}

	lastseen, err := client.MinionLastseen(minion)
	if err != nil {
		displayError(err, 1)
	}

	// Ignore errors about missing queue directory
	taskQueue, err := client.MinionTaskQueue(minion)
	if err != nil {
		if eerr, ok := err.(etcdclient.Error); !ok || eerr.Code != etcdclient.ErrorCodeKeyNotFound {
			displayError(err, 1)
		}
	}

	// Ignore errors about missing log directory
	taskLog, err := client.MinionTaskLog(minion)
	if err != nil {
		if eerr, ok := err.(etcdclient.Error); !ok || eerr.Code != etcdclient.ErrorCodeKeyNotFound {
			displayError(err, 1)
		}
	}

	// Ignore errors about missing classifier directory
	classifierKeys, err := client.MinionClassifierKeys(minion)
	if err != nil {
		if eerr, ok := err.(etcdclient.Error); !ok || eerr.Code != etcdclient.ErrorCodeKeyNotFound {
			displayError(err, 1)
		}
	}

	fmt.Printf("%-15s: %s\n", "Minion", minion)
	fmt.Printf("%-15s: %s\n", "Name", name)
	fmt.Printf("%-15s: %s\n", "Lastseen", time.Unix(lastseen, 0))
	fmt.Printf("%-15s: %d task(s)\n", "Queue", len(taskQueue))
	fmt.Printf("%-15s: %d task(s)\n", "Processed", len(taskLog))
	fmt.Printf("%-15s: %d key(s)\n", "Classifier", len(classifierKeys))
}

// Executes the "minion serve" command
func minionServeCommand(c *cli.Context) {
	hostname, err := os.Hostname()
	if err != nil {
		displayError(err, 1)
	}

	cfg := etcdConfigFromFlags(c)
	m := minion.NewEtcdMinion(hostname, cfg)
	m.Serve()
}
<|endoftext|>