From 77372fe3cc3e505b0228cbece89555df875ab2e5 Mon Sep 17 00:00:00 2001 From: Matt Jaffee Date: Thu, 9 Feb 2017 16:51:45 -0600 Subject: [PATCH] remove creator/bagent/bspawn --- bench/LICENSE | 30 -- bench/bench.go | 41 -- bench/client.go | 67 --- bench/diagonal.go | 105 ----- bench/doc.go | 39 -- bench/errgroup.go | 67 --- bench/errgroup_test.go | 176 -------- bench/import.go | 203 --------- bench/import_test.go | 136 ------ bench/multidb.go | 97 ---- bench/permutations.go | 64 --- bench/prettify.go | 41 -- bench/prettify_test.go | 93 ---- bench/query_test.go | 20 - bench/random.go | 117 ----- bench/sliceheight.go | 151 ------- bench/stats.go | 65 --- bench/store.go | 39 -- bench/zipf.go | 191 -------- build/build.go | 29 -- cmd/pilosactl/import.json | 9 - cmd/pilosactl/main.go | 692 ----------------------------- cmd/pilosactl/multidbspawn.json | 8 - cmd/pilosactl/randSetAndQuery.json | 11 - cmd/pilosactl/randspawn.json | 8 - cmd/pilosactl/slice-height.json | 8 - cmd/pilosactl/spawn.json | 14 - cmd/pilosactl/zipfspawn.json | 12 - creator/creator.go | 11 - creator/local.go | 94 ---- creator/remote.go | 187 -------- 31 files changed, 2825 deletions(-) delete mode 100644 bench/LICENSE delete mode 100644 bench/bench.go delete mode 100644 bench/client.go delete mode 100644 bench/diagonal.go delete mode 100644 bench/doc.go delete mode 100644 bench/errgroup.go delete mode 100644 bench/errgroup_test.go delete mode 100644 bench/import.go delete mode 100644 bench/import_test.go delete mode 100644 bench/multidb.go delete mode 100644 bench/permutations.go delete mode 100644 bench/prettify.go delete mode 100644 bench/prettify_test.go delete mode 100644 bench/query_test.go delete mode 100644 bench/random.go delete mode 100644 bench/sliceheight.go delete mode 100644 bench/stats.go delete mode 100644 bench/store.go delete mode 100644 bench/zipf.go delete mode 100644 build/build.go delete mode 100644 cmd/pilosactl/import.json delete mode 100644 cmd/pilosactl/multidbspawn.json delete mode 100644 cmd/pilosactl/randSetAndQuery.json delete mode 100644 cmd/pilosactl/randspawn.json delete mode 100644 cmd/pilosactl/slice-height.json delete mode 100644 cmd/pilosactl/spawn.json delete mode 100644 cmd/pilosactl/zipfspawn.json delete mode 100644 creator/creator.go delete mode 100644 creator/local.go delete mode 100644 creator/remote.go diff --git a/bench/LICENSE b/bench/LICENSE deleted file mode 100644 index e9de11fa5..000000000 --- a/bench/LICENSE +++ /dev/null @@ -1,30 +0,0 @@ -The following applies to the files errgroup.go and errgroup_test.go -in the same directory this file resides: - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/bench/bench.go b/bench/bench.go deleted file mode 100644 index 000909bde..000000000 --- a/bench/bench.go +++ /dev/null @@ -1,41 +0,0 @@ -package bench - -import "context" - -// Benchmark is an interface to guide the creation of new pilosa benchmarks or -// benchmark components. It defines 2 methods, Init, and Run. These are separate -// methods so that benchmark running code can time only the running of the -// benchmark, and not any setup. -type Benchmark interface { - // Init takes a list of hosts and an agent number. It is generally expected - // to set up a connection to pilosa using whatever client it chooses. The - // agentNum should be used to parameterize the benchmark's configuration if - // it is being run simultaneously on multiple "agents". E.G. the agentNum - // might be used to make a random seed different for each agent, or have - // each agent set a different set of bits. Init's doc string should document - // how the agentNum affects it. Every benchmark should have a 'Name' field - // set by init, which appears when the benchmark is marshalled to json as - // "name". - Init(hosts []string, agentNum int) error - - // Run runs the benchmark. The return value of Run is kept generic so that - // any relevant statistics or metrics that may be specific to the benchmark - // in question can be reported. TODO guidelines for what gets included in - // results and what will get added by other stuff. Run does not need to - // report total run time in `results`, as that will be added by calling - // code. - Run(ctx context.Context) map[string]interface{} -} - -// Command extends Benchmark by adding methods for configuring via command line flags and returning usage information. -type Command interface { - Benchmark - - // ConsumeFlags sets and parses flags, and then returns flagSet.Args(). This - // is so that multiple benchmarks can be specified at the command line. - ConsumeFlags(args []string) ([]string, error) - - // Usage returns information on how to use this benchmark. The usage string - // should explain how the agent num affects the benchmark's operation. - Usage() string -} diff --git a/bench/client.go b/bench/client.go deleted file mode 100644 index 101f62d4b..000000000 --- a/bench/client.go +++ /dev/null @@ -1,67 +0,0 @@ -package bench - -import ( - "context" - "errors" - "fmt" - "github.com/pilosa/pilosa" -) - -func firstHostClient(hosts []string) (*pilosa.Client, error) { - client, err := pilosa.NewClient(hosts[0]) - if err != nil { - return nil, err - } - return client, nil -} - -func roundRobinClient(hosts []string, agentNum int) (*pilosa.Client, error) { - clientNum := agentNum % len(hosts) - return firstHostClient(hosts[clientNum:]) -} - -// HasClient provides a reusable component for Benchmark implementations which -// provides the Init method, a ClientType argument and a cli internal variable. -type HasClient struct { - client *pilosa.Client - ClientType string `json:"client-type"` - ContentType string `json:"content-type"` -} - -// Init for HasClient looks at the ClientType field and creates a pilosa client -// either using the first host in the list of hosts or based on the agent -// number mod len(hosts) -func (h *HasClient) Init(hosts []string, agentNum int) error { - var err error - switch h.ClientType { - case "single": - h.client, err = firstHostClient(hosts) - case "round_robin": - h.client, err = roundRobinClient(hosts, agentNum) - default: - err = fmt.Errorf("Unsupported ClientType: %v", h.ClientType) - } - if err != nil { - return err - } - - switch h.ContentType { - case "protobuf": - return nil - case "pql": - return nil - default: - return fmt.Errorf("Unsupported ContentType: %v", h.ContentType) - - } -} - -func (h *HasClient) ExecuteQuery(contentType, db, query string, ctx context.Context) (interface{}, error) { - if contentType == "protobuf" { - return h.client.ExecuteQuery(ctx, db, query, true) - } else if contentType == "pql" { - return h.client.ExecutePQL(ctx, db, query) - } else { - return nil, errors.New("unsupport content type") - } -} diff --git a/bench/diagonal.go b/bench/diagonal.go deleted file mode 100644 index 393b732b3..000000000 --- a/bench/diagonal.go +++ /dev/null @@ -1,105 +0,0 @@ -package bench - -import ( - "fmt" - - "flag" - "io/ioutil" - - "context" - "time" -) - -// DiagonalSetBits sets bits with increasing profile id and bitmap id. -type DiagonalSetBits struct { - HasClient - Name string `json:"name"` - BaseBitmapID int `json:"base-bitmap-id"` - BaseProfileID int `json:"base-profile-id"` - Iterations int `json:"iterations"` - DB string `json:"db"` -} - -// Init sets up the pilosa client and modifies the configured values based on -// the agent num. -func (b *DiagonalSetBits) Init(hosts []string, agentNum int) error { - b.Name = "diagonal-set-bits" - b.BaseBitmapID = b.BaseBitmapID + (agentNum * b.Iterations) - b.BaseProfileID = b.BaseProfileID + (agentNum * b.Iterations) - return b.HasClient.Init(hosts, agentNum) -} - -// Usage returns the usage message to be printed. -func (b *DiagonalSetBits) Usage() string { - return ` -diagonal-set-bits sets bits with increasing profile id and bitmap id. - -Agent num offsets both the base profile id and base bitmap id by the number of -iterations, so that only bits on the main diagonal are set, and agents don't -overlap at all. - -Usage: diagonal-set-bits [arguments] - -The following arguments are available: - - -base-bitmap-id int - bits being set will all be greater than BaseBitmapID - - -base-profile-id int - profile id num to start from - - -iterations int - number of bits to set - - -db string - pilosa db to use - - -client-type string - Can be 'single' (all agents hitting one host) or 'round_robin' - - -content-type string - protobuf or pql -`[1:] -} - -// ConsumeFlags parses all flags up to the next non flag argument (argument does -// not start with "-" and isn't the value of a flag). It returns the remaining -// args. -func (b *DiagonalSetBits) ConsumeFlags(args []string) ([]string, error) { - fs := flag.NewFlagSet("DiagonalSetBits", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - fs.IntVar(&b.BaseBitmapID, "base-bitmap-id", 0, "") - fs.IntVar(&b.BaseProfileID, "base-profile-id", 0, "") - fs.IntVar(&b.Iterations, "iterations", 100, "") - fs.StringVar(&b.DB, "db", "benchdb", "") - fs.StringVar(&b.ClientType, "client-type", "single", "") - fs.StringVar(&b.ContentType, "content-type", "protobuf", "") - - if err := fs.Parse(args); err != nil { - return nil, err - } - return fs.Args(), nil -} - -// Run runs the DiagonalSetBits benchmark -func (b *DiagonalSetBits) Run(ctx context.Context) map[string]interface{} { - results := make(map[string]interface{}) - if b.client == nil { - results["error"] = fmt.Errorf("No client set for DiagonalSetBits") - return results - } - s := NewStats() - var start time.Time - for n := 0; n < b.Iterations; n++ { - query := fmt.Sprintf("SetBit(%d, 'frame.n', %d)", b.BaseBitmapID+n, b.BaseProfileID+n) - start = time.Now() - _, err := b.client.ExecuteQuery(ctx, b.DB, query, true) - if err != nil { - results["error"] = err - return results - } - s.Add(time.Now().Sub(start)) - } - AddToResults(s, results) - return results -} diff --git a/bench/doc.go b/bench/doc.go deleted file mode 100644 index 6554ffe80..000000000 --- a/bench/doc.go +++ /dev/null @@ -1,39 +0,0 @@ -// bench contains benchmarks and common utilities useful to benchmarks -// -// In order to write new benchmarks, one must satisfy the Benchmark and Command -// interfaces in bench.go. In order to use the benchmark from pilosactl, it -// needs to be wired in in two places. The first is BagentCommand.ParseFlags, -// where a case statement needs to be added, and the second is just adding the -// benchmark to the BagentCommand.Usage usage string. -// -// When writing a new benchmark, there are a few things to keep in mind other -// than just implementing the interface: -// -// 1. The benchmark should modify its own configuration in its Init method based -// on the agentNum it is given. How it modifies is specific to the benchmark, -// but the idea is that it should make sense to call the benchmark with the same -// configuration, but multiple different agent numbers, and it should do useful -// work each time (i.e. not just setting the same bits, or running the same -// queries). -// -// 2. The Init method should do everything that needs to be done to get the -// benchmark to a runnable state - all code in run should be the stuff that we -// actually want to time. -// -// 3. The Run method does not need to report the total runtime - that is collected -// by calling code. -// -// 4. Usage should follow the format in other benchmarks, and explain how the -// benchmark uses agentNum to modify its behavior -// -// -// Files: -// -// 1. client.go contains pilosa client code which is shared by many benchmarks -// -// 2. errgroup.go contains the ErrGroup implementation copied from golang.org/x/ -// so as not to pull in a bunch of useless deps. -// -// 3. stats.go contains useful code for gathering stats about a series of timed -// operations. -package bench diff --git a/bench/errgroup.go b/bench/errgroup.go deleted file mode 100644 index ac0bb4819..000000000 --- a/bench/errgroup.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package errgroup provides synchronization, error propagation, and Context -// cancelation for groups of goroutines working on subtasks of a common task. -package bench - -import ( - "sync" - - "context" -) - -// An ErrGroup is a collection of goroutines working on subtasks that are part of -// the same overall task. -// -// A zero ErrGroup is valid and does not cancel on error. -type ErrGroup struct { - cancel func() - - wg sync.WaitGroup - - errOnce sync.Once - err error -} - -// WithContext returns a new ErrGroup and an associated Context derived from ctx. -// -// The derived Context is canceled the first time a function passed to Go -// returns a non-nil error or the first time Wait returns, whichever occurs -// first. -func WithContext(ctx context.Context) (*ErrGroup, context.Context) { - ctx, cancel := context.WithCancel(ctx) - return &ErrGroup{cancel: cancel}, ctx -} - -// Wait blocks until all function calls from the Go method have returned, then -// returns the first non-nil error (if any) from them. -func (g *ErrGroup) Wait() error { - g.wg.Wait() - if g.cancel != nil { - g.cancel() - } - return g.err -} - -// Go calls the given function in a new goroutine. -// -// The first call to return a non-nil error cancels the group; its error will be -// returned by Wait. -func (g *ErrGroup) Go(f func() error) { - g.wg.Add(1) - - go func() { - defer g.wg.Done() - - if err := f(); err != nil { - g.errOnce.Do(func() { - g.err = err - if g.cancel != nil { - g.cancel() - } - }) - } - }() -} diff --git a/bench/errgroup_test.go b/bench/errgroup_test.go deleted file mode 100644 index 546cceb60..000000000 --- a/bench/errgroup_test.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package bench_test - -import ( - "errors" - "fmt" - "net/http" - "os" - "testing" - - "context" - "github.com/pilosa/pilosa/bench" -) - -var ( - Web = fakeSearch("web") - Image = fakeSearch("image") - Video = fakeSearch("video") -) - -type Result string -type Search func(ctx context.Context, query string) (Result, error) - -func fakeSearch(kind string) Search { - return func(_ context.Context, query string) (Result, error) { - return Result(fmt.Sprintf("%s result for %q", kind, query)), nil - } -} - -// JustErrors illustrates the use of a ErrGroup in place of a sync.WaitGroup to -// simplify goroutine counting and error handling. This example is derived from -// the sync.WaitGroup example at https://golang.org/pkg/sync/#example_WaitGroup. -func ExampleGroup_justErrors() { - var g bench.ErrGroup - var urls = []string{ - "http://www.golang.org/", - "http://www.google.com/", - "http://www.somestupidname.com/", - } - for _, url := range urls { - // Launch a goroutine to fetch the URL. - url := url // https://golang.org/doc/faq#closures_and_goroutines - g.Go(func() error { - // Fetch the URL. - resp, err := http.Get(url) - if err == nil { - resp.Body.Close() - } - return err - }) - } - // Wait for all HTTP fetches to complete. - if err := g.Wait(); err == nil { - fmt.Println("Successfully fetched all URLs.") - } -} - -// Parallel illustrates the use of a ErrGroup for synchronizing a simple parallel -// task: the "Google Search 2.0" function from -// https://talks.golang.org/2012/concurrency.slide#46, augmented with a Context -// and error-handling. -func ExampleGroup_parallel() { - Google := func(ctx context.Context, query string) ([]Result, error) { - g, ctx := bench.WithContext(ctx) - - searches := []Search{Web, Image, Video} - results := make([]Result, len(searches)) - for i, search := range searches { - i, search := i, search // https://golang.org/doc/faq#closures_and_goroutines - g.Go(func() error { - result, err := search(ctx, query) - if err == nil { - results[i] = result - } - return err - }) - } - if err := g.Wait(); err != nil { - return nil, err - } - return results, nil - } - - results, err := Google(context.Background(), "golang") - if err != nil { - fmt.Fprintln(os.Stderr, err) - return - } - for _, result := range results { - fmt.Println(result) - } - - // Output: - // web result for "golang" - // image result for "golang" - // video result for "golang" -} - -func TestZeroGroup(t *testing.T) { - err1 := errors.New("errgroup_test: 1") - err2 := errors.New("errgroup_test: 2") - - cases := []struct { - errs []error - }{ - {errs: []error{}}, - {errs: []error{nil}}, - {errs: []error{err1}}, - {errs: []error{err1, nil}}, - {errs: []error{err1, nil, err2}}, - } - - for _, tc := range cases { - var g bench.ErrGroup - - var firstErr error - for i, err := range tc.errs { - err := err - g.Go(func() error { return err }) - - if firstErr == nil && err != nil { - firstErr = err - } - - if gErr := g.Wait(); gErr != firstErr { - t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+ - "g.Wait() = %v; want %v", - g, tc.errs[:i+1], err, firstErr) - } - } - } -} - -func TestWithContext(t *testing.T) { - errDoom := errors.New("group_test: doomed") - - cases := []struct { - errs []error - want error - }{ - {want: nil}, - {errs: []error{nil}, want: nil}, - {errs: []error{errDoom}, want: errDoom}, - {errs: []error{errDoom, nil}, want: errDoom}, - } - - for _, tc := range cases { - g, ctx := bench.WithContext(context.Background()) - - for _, err := range tc.errs { - err := err - g.Go(func() error { return err }) - } - - if err := g.Wait(); err != tc.want { - t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+ - "g.Wait() = %v; want %v", - g, tc.errs, err, tc.want) - } - - canceled := false - select { - case <-ctx.Done(): - canceled = true - default: - } - if !canceled { - t.Errorf("after %T.Go(func() error { return err }) for err in %v\n"+ - "ctx.Done() was not closed", - g, tc.errs) - } - } -} diff --git a/bench/import.go b/bench/import.go deleted file mode 100644 index 2d88b23a3..000000000 --- a/bench/import.go +++ /dev/null @@ -1,203 +0,0 @@ -package bench - -import ( - "context" - "flag" - "fmt" - "io" - "io/ioutil" - "math/rand" - - "sort" - - "github.com/pilosa/pilosa/pilosactl" -) - -// NewImport returns an Import Benchmark which pilosactl importer configured. -func NewImport(stdin io.Reader, stdout, stderr io.Writer) *Import { - return &Import{ - ImportCommand: pilosactl.NewImportCommand(stdin, stdout, stderr), - } -} - -// Import sets bits with increasing profile id and bitmap id. -type Import struct { - Name string `json:"name"` - BaseBitmapID int64 `json:"base-bitmap-id"` - MaxBitmapID int64 `json:"max-bitmap-id"` - BaseProfileID int64 `json:"base-profile-id"` - MaxProfileID int64 `json:"max-profile-id"` - RandomBitmapOrder bool `json:"random-bitmap-order"` - MinBitsPerMap int64 `json:"min-bits-per-map"` - MaxBitsPerMap int64 `json:"max-bits-per-map"` - AgentControls string `json:"agent-controls"` - Seed int64 `json:"seed"` - numbits int - - *pilosactl.ImportCommand -} - -// Usage returns the usage message to be printed. -func (b *Import) Usage() string { - return ` -import generates an import file and imports using pilosa's bulk import interface - -Agent num can have various effects - see -agent-controls flag. - -Usage: import [arguments] - -The following arguments are available: - - -base-bitmap-id int - bits being set will all be greater than this - - -max-bitmap-id int - bits being set will all be less than this - - -base-profile-id int - profile id num to start from - - -max-profile-id int - maximum profile id to generate - - -random-bitmap-order - if this option is set, the import file will not be sorted by bitmap id - - -min-bits-per-map int - minimum number of bits set per bitmap - - -max-bits-per-map int - maximum number of bits set per bitmap - - -agent-controls string - can be 'height', 'width', or empty (TODO or square?)- increasing - number of agents modulates bitmap id range, profile id range, - or just sets more bits in the same range. - - -seed int - seed for RNG - - -db string - pilosa db to use - - -frame string - frame to import into -`[1:] -} - -// ConsumeFlags parses all flags up to the next non flag argument (argument does -// not start with "-" and isn't the value of a flag). It returns the remaining -// args. -func (b *Import) ConsumeFlags(args []string) ([]string, error) { - fs := flag.NewFlagSet("Import", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - fs.Int64Var(&b.BaseBitmapID, "base-bitmap-id", 0, "") - fs.Int64Var(&b.MaxBitmapID, "max-bitmap-id", 1000, "") - fs.Int64Var(&b.BaseProfileID, "base-profile-id", 0, "") - fs.Int64Var(&b.MaxProfileID, "max-profile-id", 1000, "") - fs.BoolVar(&b.RandomBitmapOrder, "random-bitmap-order", false, "") - fs.Int64Var(&b.MinBitsPerMap, "min-bits-per-map", 0, "") - fs.Int64Var(&b.MaxBitsPerMap, "max-bits-per-map", 10, "") - fs.StringVar(&b.AgentControls, "agent-controls", "", "") - fs.Int64Var(&b.Seed, "seed", 0, "") - fs.StringVar(&b.Database, "db", "benchdb", "") - fs.StringVar(&b.Frame, "frame", "testframe", "") - fs.IntVar(&b.BufferSize, "buffer-size", 10000000, "") - - if err := fs.Parse(args); err != nil { - return nil, err - } - return fs.Args(), nil -} - -// Init generates import data based on the agent num and fields of 'b'. -func (b *Import) Init(hosts []string, agentNum int) error { - if len(hosts) == 0 { - return fmt.Errorf("Need at least one host") - } - b.Name = "import" - b.Host = hosts[0] - // generate csv data - b.Seed = b.Seed + int64(agentNum) - switch b.AgentControls { - case "height": - numBitmapIDs := (b.MaxBitmapID - b.BaseBitmapID) - b.BaseBitmapID = b.BaseBitmapID + (numBitmapIDs * int64(agentNum)) - b.MaxBitmapID = b.BaseBitmapID + numBitmapIDs - case "width": - numProfileIDs := (b.MaxProfileID - b.BaseProfileID) - b.BaseProfileID = b.BaseProfileID + (numProfileIDs * int64(agentNum)) - b.MaxProfileID = b.BaseProfileID + numProfileIDs - case "": - break - default: - return fmt.Errorf("agent-controls: '%v' is not supported", b.AgentControls) - } - f, err := ioutil.TempFile("", "") - if err != nil { - return err - } - // set b.Paths) - num := GenerateImportCSV(f, b.BaseBitmapID, b.MaxBitmapID, b.BaseProfileID, b.MaxProfileID, - b.MinBitsPerMap, b.MaxBitsPerMap, b.Seed, b.RandomBitmapOrder) - b.numbits = num - // set b.Paths - b.Paths = []string{f.Name()} - return f.Close() -} - -// Run runs the Import benchmark -func (b *Import) Run(ctx context.Context) map[string]interface{} { - results := make(map[string]interface{}) - results["numbits"] = b.numbits - results["db"] = b.Database - err := b.ImportCommand.Run(ctx) - - if err != nil { - results["error"] = err.Error() - } - return results -} - -// Int64Slice is a sortable slice of 64 bit signed ints -type Int64Slice []int64 - -func (s Int64Slice) Len() int { return len(s) } -func (s Int64Slice) Less(i, j int) bool { return s[i] < s[j] } -func (s Int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } - -// GenerateImportCSV writes a generated csv to 'w' which is in the form pilosactl expects for imports. -func GenerateImportCSV(w io.Writer, baseBitmapID, maxBitmapID, baseProfileID, maxProfileID, minBitsPerMap, maxBitsPerMap, seed int64, randomOrder bool) int { - src := rand.NewSource(seed) - rng := rand.New(src) - - var bitmapIDs []int - if randomOrder { - bitmapIDs = rng.Perm(int(maxBitmapID - baseBitmapID)) - } - numrows := 0 - profileIDs := make(Int64Slice, maxBitsPerMap) - for i := baseBitmapID; i < maxBitmapID; i++ { - var bitmapID int64 - if randomOrder { - bitmapID = int64(bitmapIDs[i-baseBitmapID]) - } else { - bitmapID = int64(i) - } - - numBitsToSet := rng.Int63n(maxBitsPerMap-minBitsPerMap) + minBitsPerMap - numrows += int(numBitsToSet) - for j := int64(0); j < numBitsToSet; j++ { - profileIDs[j] = rng.Int63n(maxProfileID-baseProfileID) + baseProfileID - } - profIDs := profileIDs[:numBitsToSet] - if !randomOrder { - sort.Sort(profIDs) - } - for j := int64(0); j < numBitsToSet; j++ { - fmt.Fprintf(w, "%d,%d\n", bitmapID, profIDs[j]) - } - - } - return numrows -} diff --git a/bench/import_test.go b/bench/import_test.go deleted file mode 100644 index 07074f125..000000000 --- a/bench/import_test.go +++ /dev/null @@ -1,136 +0,0 @@ -package bench_test - -import ( - "bytes" - "testing" - - "io/ioutil" - - "os" - - "github.com/pilosa/pilosa/bench" -) - -func TestImportInit(t *testing.T) { - imp := bench.NewImport(os.Stdin, os.Stdout, os.Stderr) - imp.BaseBitmapID = 0 - imp.MaxBitmapID = 10 - imp.BaseProfileID = 0 - imp.MaxProfileID = 10 - imp.RandomBitmapOrder = false - imp.MinBitsPerMap = 2 - imp.MaxBitsPerMap = 3 - imp.AgentControls = "width" - imp.Seed = 0 - - imp.Init([]string{"blah"}, 2) - f, err := os.Open(imp.Paths[0]) - if err != nil { - t.Fatalf("Couldn't open file: %v, err: %v", imp.Paths[0], err) - } - bytes, err := ioutil.ReadAll(f) - if err != nil { - t.Fatalf("error reading file: %v", err) - } - - expected := ` -0,21 -0,22 -1,20 -1,22 -2,22 -2,26 -3,21 -3,23 -4,21 -4,22 -5,20 -5,28 -6,23 -6,27 -7,20 -7,20 -8,23 -8,29 -9,23 -9,29 -`[1:] - - if string(bytes) != expected { - t.Fatalf("unexpected result: %v", string(bytes)) - } -} - -func TestGenerateImportCSVNonRand(t *testing.T) { - b := bytes.NewBuffer(make([]byte, 0)) - - bench.GenerateImportCSV(b, 0, 10, 20, 30, 2, 3, 2, false) - - bytes, err := ioutil.ReadAll(b) - if err != nil { - t.Fatalf("Error reading buffer: %v", err) - } - - expected := ` -0,21 -0,22 -1,20 -1,22 -2,22 -2,26 -3,21 -3,23 -4,21 -4,22 -5,20 -5,28 -6,23 -6,27 -7,20 -7,20 -8,23 -8,29 -9,23 -9,29 -`[1:] - - if string(bytes) != expected { - t.Fatalf("unexpected value for generated csv: \n%v", string(bytes)) - } -} - -func TestGenerateImportCSVRand(t *testing.T) { - b := bytes.NewBuffer(make([]byte, 0)) - - bench.GenerateImportCSV(b, 0, 10, 21, 29, 1, 4, 0, true) - - bytes, err := ioutil.ReadAll(b) - if err != nil { - t.Fatalf("Error reading buffer: %v", err) - } - - expected := ` -8,25 -2,23 -3,22 -3,28 -3,28 -0,25 -0,23 -5,26 -5,23 -7,21 -7,23 -1,25 -6,23 -6,27 -6,25 -9,26 -4,22 -4,24 -`[1:] - - if string(bytes) != expected { - t.Fatalf("unexpected value for generated csv: \n%v", string(bytes)) - } -} diff --git a/bench/multidb.go b/bench/multidb.go deleted file mode 100644 index aa275ac17..000000000 --- a/bench/multidb.go +++ /dev/null @@ -1,97 +0,0 @@ -package bench - -import ( - "context" - "flag" - "fmt" - "io/ioutil" - "strconv" - "time" -) - -// MultiDBSetBits sets bits with increasing profile id and bitmap id. -type MultiDBSetBits struct { - HasClient - Name string `json:"name"` - BaseBitmapID int `json:"base-bitmap-id"` - BaseProfileID int `json:"base-profile-id"` - Iterations int `json:"iterations"` - Database string `json:"database"` -} - -// Init sets up the db name based on the agentNum and sets up the pilosa client. -func (b *MultiDBSetBits) Init(hosts []string, agentNum int) error { - b.Name = "multi-db-set-bits" - b.Database = b.Database + strconv.Itoa(agentNum) - return b.HasClient.Init(hosts, agentNum) -} - -// Usage returns the usage message to be printed. -func (b *MultiDBSetBits) Usage() string { - return ` -multi-db-set-bits sets bits with increasing profile id and bitmap id using a different DB for each agent. - -Agent num changes the database being written to. - -Usage: multi-db-set-bits [arguments] - -The following arguments are available: - - -base-bitmap-id int - bits being set will all be greater than base-bitmap-id - - -base-profile-id int - profile id num to start from - - -iterations int - number of bits to set - - -client-type string - Can be 'single' (all agents hitting one host) or 'round_robin' - - -content-type string - protobuf or pql - -`[1:] -} - -// ConsumeFlags parses all flags up to the next non flag argument (argument does -// not start with "-" and isn't the value of a flag). It returns the remaining -// args. -func (b *MultiDBSetBits) ConsumeFlags(args []string) ([]string, error) { - fs := flag.NewFlagSet("MultiDBSetBits", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - fs.IntVar(&b.BaseBitmapID, "base-bitmap-id", 0, "") - fs.IntVar(&b.BaseProfileID, "base-profile-id", 0, "") - fs.IntVar(&b.Iterations, "iterations", 100, "") - fs.StringVar(&b.ClientType, "client-type", "single", "") - fs.StringVar(&b.ContentType, "content-type", "protobuf", "") - - if err := fs.Parse(args); err != nil { - return nil, err - } - return fs.Args(), nil -} - -// Run runs the MultiDBSetBits benchmark -func (b *MultiDBSetBits) Run(ctx context.Context) map[string]interface{} { - results := make(map[string]interface{}) - if b.client == nil { - results["error"] = fmt.Errorf("No client set for MultiDBSetBits") - return results - } - s := NewStats() - var start time.Time - for n := 0; n < b.Iterations; n++ { - query := fmt.Sprintf("SetBit(%d, 'frame.n', %d)", b.BaseBitmapID+n, b.BaseProfileID+n) - start = time.Now() - _, err := b.client.ExecuteQuery(ctx, b.Database, query, true) - if err != nil { - results["error"] = err - return results - } - s.Add(time.Now().Sub(start)) - } - AddToResults(s, results) - return results -} diff --git a/bench/permutations.go b/bench/permutations.go deleted file mode 100644 index cbe4162e4..000000000 --- a/bench/permutations.go +++ /dev/null @@ -1,64 +0,0 @@ -package bench - -// PermutationGenerator provides a way to pass integer IDs through a permutation -// map that is pseudorandom but repeatable. This could be done with rand.Perm, -// but that would require storing a [Iterations]int64 array, which we want to avoid -// for large values of Iterations. -// It works by using a Linear Congruence Generator (https://en.wikipedia.org/wiki/Linear_congruential_generator) -// with modulus m = Iterations, -// c = an arbitrary prime, -// a = computed to ensure the full period. -// relevant stackoverflow: http://cs.stackexchange.com/questions/29822/lazily-computing-a-random-permutation-of-the-positive-integers -type PermutationGenerator struct { - a int64 - c int64 - m int64 -} - -func NewPermutationGenerator(m int64, seed int64) *PermutationGenerator { - // figure out 'a' and 'c', return PermutationGenerator - a := LCGmultiplierFromModulus(m, seed) - c := int64(22695479) - return &PermutationGenerator{a, c, m} -} - -func (p *PermutationGenerator) Next(n int64) int64 { - // run one step of the LCG - return (n*p.a + p.c) % p.m -} - -// LCG parameters must satisfy three conditions: -// 1. m and c are relatively prime (satisfied for prime c != m) -// 2. a-1 is divisible by all prime factors of m -// 3. a-1 is divisible by 4 if m is divisible by 4 -// Additionally, a seed can be used to select between different permutations -func LCGmultiplierFromModulus(m int64, seed int64) int64 { - factors := primeFactors(m) - product := int64(1) - for p := range factors { - // satisfy condition 2 - product *= p - } - - if m%4 == 0 { - // satisfy condition 3 - product *= 2 - } - - return product*seed + 1 -} - -// Returns map of {integerFactor: count, ...} -// This is a naive algorithm that will not work well for large prime n. -func primeFactors(n int64) map[int64]int { - factors := make(map[int64]int) - for i := int64(2); i <= n; i++ { - div, mod := n/i, n%i - for mod == 0 { - factors[i] += 1 - n = div - div, mod = n/i, n%i - } - } - return factors -} diff --git a/bench/prettify.go b/bench/prettify.go deleted file mode 100644 index 1902d412b..000000000 --- a/bench/prettify.go +++ /dev/null @@ -1,41 +0,0 @@ -package bench - -import "time" - -// wrapper type to force human-readable JSON output -type PrettyDuration time.Duration - -// MarshalJSON returns a nicely formatted duration, instead of it just being -// treated like an int. -func (d PrettyDuration) MarshalJSON() ([]byte, error) { - s := time.Duration(d).String() - return []byte("\"" + s + "\""), nil -} - -// Recursively replaces elements of ugly types with their pretty wrappers -func Prettify(m map[string]interface{}) map[string]interface{} { - newmap := make(map[string]interface{}) - for k, v := range m { - switch v.(type) { - case map[string]interface{}: - newmap[k] = Prettify(v.(map[string]interface{})) - case []time.Duration: - newslice := make([]PrettyDuration, len(v.([]time.Duration))) - slice := v.([]time.Duration) - for n, e := range slice { - newslice[n] = PrettyDuration(e) - } - newmap[k] = newslice - case time.Duration: - newmap[k] = PrettyDuration(v.(time.Duration)) - default: - if interv, ok := v.([]map[string]interface{}); ok { - for i, iv := range interv { - interv[i] = Prettify(iv) - } - } - newmap[k] = v - } - } - return newmap -} diff --git a/bench/prettify_test.go b/bench/prettify_test.go deleted file mode 100644 index 20269d73e..000000000 --- a/bench/prettify_test.go +++ /dev/null @@ -1,93 +0,0 @@ -package bench_test - -import ( - "bytes" - "encoding/json" - "fmt" - "os" - "testing" - "time" - - "github.com/pilosa/pilosa/bench" -) - -func prettyEncode(data map[string]interface{}) string { - pretty := bench.Prettify(data) - jsonString := new(bytes.Buffer) - enc := json.NewEncoder(jsonString) - enc.SetIndent("", " ") - err := enc.Encode(pretty) - if err != nil { - fmt.Fprintln(os.Stderr, err) - } - - return jsonString.String() -} - -func TestPrettifyString(t *testing.T) { - res := make(map[string]interface{}, 1) - res["0"] = "foobar" - pretty := prettyEncode(res) - - expected := ` -{ - "0": "foobar" -} -`[1:] - - if pretty != expected { - t.Fatalf("Pretty string doesn't match") - } -} - -func TestPrettifyInt(t *testing.T) { - res := make(map[string]interface{}, 1) - res["0"] = 234567 - pretty := prettyEncode(res) - - expected := ` -{ - "0": 234567 -} -`[1:] - - if pretty != expected { - t.Fatalf("Pretty int doesn't match") - } -} - -func TestPrettifyDuration(t *testing.T) { - res := make(map[string]interface{}, 1) - res["0"] = time.Duration(234567) - pretty := prettyEncode(res) - - expected := ` -{ - "0": "234.567µs" -} -`[1:] - - if pretty != expected { - t.Fatalf("Pretty duration doesn't match") - } -} - -func TestPrettifyDurationSlice(t *testing.T) { - res := make(map[string]interface{}, 1) - res["0"] = []time.Duration{123, 234567, 34567890} - pretty := prettyEncode(res) - - expected := ` -{ - "0": [ - "123ns", - "234.567µs", - "34.56789ms" - ] -} -`[1:] - - if pretty != expected { - t.Fatalf("Pretty duration slice doesn't match") - } -} diff --git a/bench/query_test.go b/bench/query_test.go deleted file mode 100644 index 13b0ad416..000000000 --- a/bench/query_test.go +++ /dev/null @@ -1,20 +0,0 @@ -package bench_test - -import ( - "github.com/pilosa/pilosa/bench" - "testing" -) - -func TestRandomBitmapCall(t *testing.T) { - qm := bench.NewQueryGenerator(5) - bmc := qm.RandomBitmapCall(4, 3, 0, 1000) - t.Log(bmc.String()) -} - -func TestRandom(t *testing.T) { - for i := 99; i < 120; i++ { - qm := bench.NewQueryGenerator(int64(i)) - call := qm.Random(10, 4, 3, 0, 1000) - t.Log(call) - } -} diff --git a/bench/random.go b/bench/random.go deleted file mode 100644 index 3bb74e886..000000000 --- a/bench/random.go +++ /dev/null @@ -1,117 +0,0 @@ -package bench - -import ( - "fmt" - - "flag" - "io/ioutil" - - "context" - "math/rand" - "time" -) - -// RandomSetBits sets bits randomly and deterministically based on a seed. -type RandomSetBits struct { - HasClient - Name string `json:"name"` - BaseBitmapID int64 `json:"base-bitmap-id"` - BaseProfileID int64 `json:"base-profile-id"` - BitmapIDRange int64 `json:"bitmap-id-range"` - ProfileIDRange int64 `json:"profile-id-range"` - Iterations int `json:"iterations"` - Seed int64 `json:"seed"` - DB string `json:"db"` -} - -// Init adds the agent num to the random seed and initializes the client. -func (b *RandomSetBits) Init(hosts []string, agentNum int) error { - b.Name = "random-set-bits" - b.Seed = b.Seed + int64(agentNum) - return b.HasClient.Init(hosts, agentNum) -} - -// Usage returns the usage message to be printed. -func (b *RandomSetBits) Usage() string { - return ` -random-set-bits sets random bits - -Agent number modifies the random seed. - -Usage: random-set-bits [arguments] - -The following arguments are available: - - -base-bitmap-id int - bits being set will all be greater than BaseBitmapID - - -bitmap-id-range int - number of possible bitmap ids that can be set - - -base-profile-id int - profile id num to start from - - -profile-id-range int - number of possible profile ids that can be set - - -iterations int - number of bits to set - - -seed int - Seed for RNG - - -db string - pilosa db to use - - -client-type string - Can be 'single' (all agents hitting one host) or 'round_robin' - - -content-type string - protobuf or pql -`[1:] -} - -// ConsumeFlags parses all flags up to the next non flag argument (argument does -// not start with "-" and isn't the value of a flag). It returns the remaining -// args. -func (b *RandomSetBits) ConsumeFlags(args []string) ([]string, error) { - fs := flag.NewFlagSet("RandomSetBits", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - fs.Int64Var(&b.BaseBitmapID, "base-bitmap-id", 0, "") - fs.Int64Var(&b.BitmapIDRange, "bitmap-id-range", 100000, "") - fs.Int64Var(&b.BaseProfileID, "base-profile-id", 0, "") - fs.Int64Var(&b.ProfileIDRange, "profile-id-range", 100000, "") - fs.Int64Var(&b.Seed, "seed", 1, "") - fs.IntVar(&b.Iterations, "iterations", 100, "") - fs.StringVar(&b.DB, "db", "benchdb", "") - fs.StringVar(&b.ClientType, "client-type", "single", "") - fs.StringVar(&b.ContentType, "content-type", "protobuf", "") - - if err := fs.Parse(args); err != nil { - return nil, err - } - return fs.Args(), nil -} - -// Run runs the RandomSetBits benchmark -func (b *RandomSetBits) Run(ctx context.Context) map[string]interface{} { - src := rand.NewSource(b.Seed) - rng := rand.New(src) - results := make(map[string]interface{}) - if b.client == nil { - results["error"] = fmt.Errorf("No client set for RandomSetBits") - return results - } - s := NewStats() - var start time.Time - for n := 0; n < b.Iterations; n++ { - bitmapID := rng.Int63n(b.BitmapIDRange) - profID := rng.Int63n(b.ProfileIDRange) - query := fmt.Sprintf("SetBit(%d, 'frame.n', %d)", b.BaseBitmapID+bitmapID, b.BaseProfileID+profID) - start = time.Now() - b.client.ExecuteQuery(ctx, b.DB, query, true) - s.Add(time.Now().Sub(start)) - } - AddToResults(s, results) - return results -} diff --git a/bench/sliceheight.go b/bench/sliceheight.go deleted file mode 100644 index 5b870aff1..000000000 --- a/bench/sliceheight.go +++ /dev/null @@ -1,151 +0,0 @@ -package bench - -import ( - "context" - "flag" - "io" - "io/ioutil" - "strconv" - "time" - - "github.com/pilosa/pilosa" - "github.com/pilosa/pilosa/pql" -) - -// NewSliceHeight creates a new slice height benchmark with stdin/out/err -// initialized. -func NewSliceHeight(stdin io.Reader, stdout, stderr io.Writer) *SliceHeight { - return &SliceHeight{ - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - } -} - -// SliceHeight benchmark tests the effect of an increasing number of bitmaps in -// a single slice on query time. -type SliceHeight struct { - MaxTime time.Duration `json:"max-time"` - hosts []string - - Name string `json:"name"` - MinBitsPerMap int64 `json:"min-bits-per-map"` - MaxBitsPerMap int64 `json:"max-bits-per-map"` - Seed int64 `json:"seed"` - Database string `json:"database"` - Frame string `json:"frame"` - - Stdin io.Reader `json:"-"` - Stdout io.Writer `json:"-"` - Stderr io.Writer `json:"-"` -} - -// Usage returns the usage message to be printed. -func (b *SliceHeight) Usage() string { - return ` -slice-height repeatedly imports more bitmaps into a single slice and tests query times in between. - -Agent number has no effect on this benchmark. - -Usage: slice-height [arguments] - -The following arguments are available: - - -max-time int - stop benchmark after this many seconds - - -min-bits-per-map int - minimum number of bits set per bitmap - - -max-bits-per-map int - maximum number of bits set per bitmap - - -seed int - seed for RNG - - -db string - pilosa db to use - - -frame string - frame to import into -`[1:] -} - -// ConsumeFlags parses all flags up to the next non flag argument (argument does -// not start with "-" and isn't the value of a flag). It returns the remaining -// args. -func (b *SliceHeight) ConsumeFlags(args []string) ([]string, error) { - fs := flag.NewFlagSet("SliceHeight", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - - maxTime := fs.Int("max-time", 30, "") - fs.Int64Var(&b.MinBitsPerMap, "min-bits-per-map", 0, "") - fs.Int64Var(&b.MaxBitsPerMap, "max-bits-per-map", 10, "") - fs.Int64Var(&b.Seed, "seed", 0, "") - fs.StringVar(&b.Database, "db", "benchdb", "") - fs.StringVar(&b.Frame, "frame", "testframe", "") - - if err := fs.Parse(args); err != nil { - return nil, err - } - b.MaxTime = time.Duration(*maxTime) * time.Second - return fs.Args(), nil -} - -// Init sets up the slice height benchmark. -func (b *SliceHeight) Init(hosts []string, agentNum int) error { - b.Name = "slice-height" - b.hosts = hosts - return nil -} - -// Run runs the SliceHeight benchmark -func (b *SliceHeight) Run(ctx context.Context) map[string]interface{} { - results := make(map[string]interface{}) - - imp := NewImport(b.Stdin, b.Stdout, b.Stderr) - imp.MaxBitmapID = 100 - imp.MaxProfileID = pilosa.SliceWidth - imp.MinBitsPerMap = b.MinBitsPerMap - imp.MaxBitsPerMap = b.MaxBitsPerMap - imp.Database = b.Database - imp.Frame = b.Frame - - start := time.Now() - - for i := 0; i > -1; i++ { - iresults := make(map[string]interface{}) - results["iteration"+strconv.Itoa(i)] = iresults - - genstart := time.Now() - imp.Init(b.hosts, 0) - gendur := time.Now().Sub(genstart) - iresults["csvgen"] = gendur - - iresults["import"] = imp.Run(ctx) - - qstart := time.Now() - q := &pql.Call{ - Name: "TopN", - Args: map[string]interface{}{ - "frame": b.Frame, - "n": 50, - }, - } - _, err := imp.Client.ExecuteQuery(ctx, b.Database, q.String(), true) - if err != nil { - iresults["query_error"] = err.Error() - } else { - qdur := time.Now().Sub(qstart) - iresults["query"] = qdur - } - imp.BaseBitmapID = imp.MaxBitmapID - imp.MaxBitmapID = imp.MaxBitmapID * 10 - - if time.Now().Sub(start) > b.MaxTime { - break - } - } - - return results -} diff --git a/bench/stats.go b/bench/stats.go deleted file mode 100644 index 7a32987f9..000000000 --- a/bench/stats.go +++ /dev/null @@ -1,65 +0,0 @@ -package bench - -import ( - "math" - "time" -) - -// Stats object helps track timing stats. -type Stats struct { - Min time.Duration - Max time.Duration - Mean time.Duration - sumSquareDelta float64 - Total time.Duration - Num int64 - All []time.Duration - SaveAll bool -} - -// NewStats gets a Stats object. -func NewStats() *Stats { - return &Stats{ - Min: 1<<63 - 1, - All: make([]time.Duration, 0), - } -} - -// Add adds a new time to the stats object. -func (s *Stats) Add(td time.Duration) { - if s.SaveAll { - s.All = append(s.All, td) - } - s.Num += 1 - s.Total += td - if td < s.Min { - s.Min = td - } - if td > s.Max { - s.Max = td - } - - // online variance calculation - // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm - delta := td - s.Mean - s.Mean += delta / time.Duration(s.Num) - s.sumSquareDelta += float64(delta * (td - s.Mean)) -} - -// Avg returns the average of all durations Added to the Stats object. -func (s *Stats) Avg() time.Duration { - return s.Total / time.Duration(s.Num) -} - -// AddToResults serializes the summary of Stats and adds them to the results -// map. -func AddToResults(s *Stats, results map[string]interface{}) { - results["min"] = s.Min - results["max"] = s.Max - results["avg"] = s.Mean - variance := s.sumSquareDelta / float64(s.Num) - results["std"] = time.Duration(math.Sqrt(variance)) - if s.SaveAll { - results["all"] = s.All - } -} diff --git a/bench/store.go b/bench/store.go deleted file mode 100644 index 6f752cc08..000000000 --- a/bench/store.go +++ /dev/null @@ -1,39 +0,0 @@ -package bench - -import ( - "fmt" - "strings" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/session" - "github.com/aws/aws-sdk-go/service/s3" -) - -// NewS3Uploader creates an S3Uploader with specified bucket and key -func NewS3Uploader(bucket string, key string) *S3Uploader { - return &S3Uploader{ - bucket, - s3.New(session.New(&aws.Config{})), - key, - } -} - -// S3Uploader is an io.Writer for sending output to AWS S3 storage. -type S3Uploader struct { - bucket string - service *s3.S3 - key string -} - -// Write writes data to the uploader's bucket/key -func (u *S3Uploader) Write(data []byte) (int, error) { - // first return value of PutObject contains an ETag (hash) of the uploaded object, not needed here - fmt.Println(string(data)) - _, err := u.service.PutObject(&s3.PutObjectInput{ - Body: strings.NewReader(string(data)), - Bucket: &u.bucket, - Key: &u.key, - }) - - return 0, err -} diff --git a/bench/zipf.go b/bench/zipf.go deleted file mode 100644 index 46832a75f..000000000 --- a/bench/zipf.go +++ /dev/null @@ -1,191 +0,0 @@ -package bench - -import ( - "fmt" - - "flag" - "io/ioutil" - - "context" - "math" - "math/rand" - "time" -) - -// Zipf sets random bits according to the Zipf-Mandelbrot distribution. -// This distribution accepts two parameters, Exponent and Ratio, for both bitmaps and profiles. -// It also uses PermutationGenerator to permute IDs randomly. -type Zipf struct { - HasClient - Name string `json:"name"` - BaseBitmapID int64 `json:"base-bitmap-id"` - BaseProfileID int64 `json:"base-profile-id"` - BitmapIDRange int64 `json:"bitmap-id-range"` - ProfileIDRange int64 `json:"profile-id-range"` - Iterations int `json:"iterations"` - Seed int64 `json:"seed"` - DB string `json:"db"` - BitmapExponent float64 `json:"bitmap-exponent"` - BitmapRatio float64 `json:"bitmap-ratio"` - ProfileExponent float64 `json:"profile-exponent"` - ProfileRatio float64 `json:"profile-ratio"` - Operation string `json:"operation"` - bitmapRng *rand.Zipf - profileRng *rand.Zipf - bitmapPerm *PermutationGenerator - profilePerm *PermutationGenerator -} - -// Usage returns the usage message to be printed. -func (b *Zipf) Usage() string { - return ` -zipf sets random bits according to the Zipf distribution. -This is a power-law distribution controlled by two parameters. -Exponent, in the range (1, inf), with a default value of 1.001, controls -the "sharpness" of the distribution, with higher exponent being sharper. -Ratio, in the range (0, 1), with a default value of 0.25, controls the -maximum variation of the distribution, with higher ratio being more uniform. - -Agent number modifies random seed. - -Usage: zipf [arguments] - -The following arguments are available: - - -base-bitmap-id int - bits being set will all be greater than BaseBitmapID - - -bitmap-id-range int - number of possible bitmap ids that can be set - - -base-profile-id int - profile id num to start from - - -profile-id-range int - number of possible profile ids that can be set - - -iterations int - number of bits to set - - -seed int - Seed for RNG - - -db string - pilosa db to use - - -bitmap-exponent float64 - zipf exponent parameter for bitmap IDs - - -bitmap-ratio float64 - zipf probability ratio parameter for bitmap IDs - - -profile-exponent float64 - zipf exponent parameter for profile IDs - - -profile-ratio float64 - zipf probability ratio parameter for profile IDs - - -client-type string - Can be 'single' (all agents hitting one host) or 'round_robin' - - -operation string - Can be 'set' or 'clear' - - -content-type string - protobuf or pql -`[1:] -} - -// ConsumeFlags parses all flags up to the next non flag argument (argument does -// not start with "-" and isn't the value of a flag). It returns the remaining -// args. -func (b *Zipf) ConsumeFlags(args []string) ([]string, error) { - fs := flag.NewFlagSet("Zipf", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - fs.Int64Var(&b.BaseBitmapID, "base-bitmap-id", 0, "") - fs.Int64Var(&b.BitmapIDRange, "bitmap-id-range", 100000, "") - fs.Int64Var(&b.BaseProfileID, "base-profile-id", 0, "") - fs.Int64Var(&b.ProfileIDRange, "profile-id-range", 100000, "") - fs.Int64Var(&b.Seed, "seed", 1, "") - fs.IntVar(&b.Iterations, "iterations", 100, "") - fs.StringVar(&b.DB, "db", "benchdb", "") - fs.Float64Var(&b.BitmapExponent, "bitmap-exponent", 1.01, "") - fs.Float64Var(&b.BitmapRatio, "bitmap-ratio", 0.25, "") - fs.Float64Var(&b.ProfileExponent, "profile-exponent", 1.01, "") - fs.Float64Var(&b.ProfileRatio, "profile-ratio", 0.25, "") - fs.StringVar(&b.ClientType, "client-type", "single", "") - fs.StringVar(&b.Operation, "operation", "set", "") - fs.StringVar(&b.ContentType, "content-type", "protobuf", "") - - if err := fs.Parse(args); err != nil { - return nil, err - } - return fs.Args(), nil -} - -// Offset is the true parameter used by the Zipf distribution, but the ratio, -// as defined here, is a simpler, readable way to define the distribution. -// Offset is in [1, inf), and its meaning depends on N (a pain for updating benchmark configs) -// ratio is in (0, 1), and its meaning does not depend on N. -// it is the ratio of the lowest probability in the distribution to the highest. -// ratio=0.01 corresponds to a very small offset - the most skewed distribution for a given pair (N, exp) -// ratio=0.99 corresponds to a very large offset - the most nearly uniform distribution for a given (N, exp) -func getZipfOffset(N int64, exp, ratio float64) float64 { - z := math.Pow(ratio, 1/exp) - return z * float64(N-1) / (1 - z) -} - -// Init sets up the benchmark based on the agent number and initializes the -// client. -func (b *Zipf) Init(hosts []string, agentNum int) error { - b.Name = "zipf" - b.Seed = b.Seed + int64(agentNum) - rnd := rand.New(rand.NewSource(b.Seed)) - bitmapOffset := getZipfOffset(b.BitmapIDRange, b.BitmapExponent, b.BitmapRatio) - b.bitmapRng = rand.NewZipf(rnd, b.BitmapExponent, bitmapOffset, uint64(b.BitmapIDRange-1)) - profileOffset := getZipfOffset(b.ProfileIDRange, b.ProfileExponent, b.ProfileRatio) - b.profileRng = rand.NewZipf(rnd, b.ProfileExponent, profileOffset, uint64(b.ProfileIDRange-1)) - - b.bitmapPerm = NewPermutationGenerator(b.BitmapIDRange, b.Seed) - b.profilePerm = NewPermutationGenerator(b.ProfileIDRange, b.Seed+1) - - if b.Operation != "set" && b.Operation != "clear" { - return fmt.Errorf("Unsupported operation: \"%s\" (must be \"set\" or \"clear\")", b.Operation) - } - - return b.HasClient.Init(hosts, agentNum) -} - -// Run runs the Zipf benchmark -func (b *Zipf) Run(ctx context.Context) map[string]interface{} { - results := make(map[string]interface{}) - if b.client == nil { - results["error"] = fmt.Errorf("No client set for Zipf") - return results - } - operation := "SetBit" - if b.Operation == "clear" { - operation = "ClearBit" - } - s := NewStats() - var start time.Time - for n := 0; n < b.Iterations; n++ { - // generate IDs from Zipf distribution - bitmapIDOriginal := b.bitmapRng.Uint64() - profIDOriginal := b.profileRng.Uint64() - // permute IDs randomly, but repeatably - bitmapID := b.bitmapPerm.Next(int64(bitmapIDOriginal)) - profID := b.profilePerm.Next(int64(profIDOriginal)) - - query := fmt.Sprintf("%s(%d, 'frame.n', %d)", operation, b.BaseBitmapID+int64(bitmapID), b.BaseProfileID+int64(profID)) - start = time.Now() - _, err := b.client.ExecuteQuery(ctx, b.DB, query, true) - if err != nil { - results["error"] = fmt.Sprintf("Error executing query in zipf: %v", err) - return results - } - s.Add(time.Now().Sub(start)) - } - AddToResults(s, results) - return results -} diff --git a/build/build.go b/build/build.go deleted file mode 100644 index eb25af446..000000000 --- a/build/build.go +++ /dev/null @@ -1,29 +0,0 @@ -package build - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "os/exec" -) - -func Binary(pkg, goos, goarch string) (io.Reader, error) { - binFile, err := ioutil.TempFile("", "pilosactl") - if err != nil { - return nil, fmt.Errorf("build binary: %v", err) - } - com := exec.Command("go", "build", "-o", binFile.Name(), pkg) - com.Env = append([]string{"GOOS=" + goos, "GOARCH=" + goarch}, os.Environ()...) - - err = com.Run() - if err != nil { - return nil, err - } - - f, err := os.Open(binFile.Name()) - if err != nil { - return nil, err - } - return f, nil -} diff --git a/cmd/pilosactl/import.json b/cmd/pilosactl/import.json deleted file mode 100644 index 8962ae063..000000000 --- a/cmd/pilosactl/import.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "benchmarks": [ - { - "num": 1, - "name": "import", - "args": ["import", "-max-bitmap-id", "100000", "-max-profile-id", "10000", "-max-bits-per-map", "100", "-seed", "0", "-agent-controls", "width", "import", "-max-bitmap-id", "100000", "-max-profile-id", "10000", "-max-bits-per-map", "100", "-seed", "0", "-agent-controls", "width", "-random-bitmap-order", "-db", "randoload"] - } - ] -} diff --git a/cmd/pilosactl/main.go b/cmd/pilosactl/main.go index bb22d14ac..88fa6fea5 100644 --- a/cmd/pilosactl/main.go +++ b/cmd/pilosactl/main.go @@ -4,7 +4,6 @@ import ( "bufio" "context" "encoding/csv" - "encoding/json" "errors" "flag" "fmt" @@ -13,27 +12,18 @@ import ( "log" "math/rand" "os" - "os/signal" "path/filepath" "sort" "strconv" "strings" - "sync" "syscall" "text/tabwriter" "time" "unsafe" "github.com/pilosa/pilosa" - "github.com/pilosa/pilosa/bench" - "github.com/pilosa/pilosa/build" - "github.com/pilosa/pilosa/creator" "github.com/pilosa/pilosa/pilosactl" "github.com/pilosa/pilosa/roaring" - pssh "github.com/pilosa/pilosa/ssh" - - "github.com/satori/go.uuid" - "golang.org/x/crypto/ssh" ) var ( @@ -115,9 +105,6 @@ The commands are: inspect inspects fragment data files check performs a consistency check of data files bench benchmarks operations - create create pilosa clusters - bagent run a benchmarking agent - bspawn create a cluster and agents and run benchmarks based on config file Use the "-h" flag with any command for more information. `) @@ -157,12 +144,6 @@ func (m *Main) ParseFlags(args []string) error { m.Cmd = NewCheckCommand(m.Stdin, m.Stdout, m.Stderr) case "bench": m.Cmd = NewBenchCommand(m.Stdin, m.Stdout, m.Stderr) - case "create": - m.Cmd = NewCreateCommand(m.Stdin, m.Stdout, m.Stderr) - case "bagent": - m.Cmd = NewBagentCommand(m.Stdin, m.Stdout, m.Stderr) - case "bspawn": - m.Cmd = NewBspawnCommand(m.Stdin, m.Stdout, m.Stderr) default: return ErrUnknownCommand } @@ -1004,672 +985,6 @@ func (cmd *BenchCommand) runSetBit(ctx context.Context, client *pilosa.Client) e return nil } -// CreateCommand represents a command for creating a pilosa cluster. -type CreateCommand struct { - ServerN int `json:"serverN"` - ReplicaN int `json:"replicaN"` - LogFilePrefix string `json:"log-file-prefix"` - Hosts []string `json:"hosts"` - GoMaxProcs int `json:"gomaxprocs"` - - SSHUser string `json:"ssh-user"` - - CopyBinary bool `json:"copy-binary"` - GOOS string `json:"goos"` - GOARCH string `json:"goarch"` - - // The following are set by CreateCommand.Run once they are known and exist - // to appear in the output - LogFiles []string `json:"log-files"` - FinalHosts []string `json:"final-hosts"` - - // Standard input/output - Stdin io.Reader `json:"-"` - Stdout io.Writer `json:"-"` - Stderr io.Writer `json:"-"` -} - -// NewCreateCommand returns a new instance of CreateCommand. -func NewCreateCommand(stdin io.Reader, stdout, stderr io.Writer) *CreateCommand { - return &CreateCommand{ - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - } -} - -// ParseFlags parses command line flags from args. -func (cmd *CreateCommand) ParseFlags(args []string) error { - fs := flag.NewFlagSet("pilosactl", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - fs.IntVar(&cmd.ServerN, "serverN", 3, "") - fs.IntVar(&cmd.ReplicaN, "replicaN", 1, "") - fs.StringVar(&cmd.LogFilePrefix, "log-file-prefix", "", "") - var hosts string - fs.IntVar(&cmd.GoMaxProcs, "gomaxprocs", 0, "") - fs.StringVar(&hosts, "hosts", "", "") - fs.StringVar(&cmd.SSHUser, "ssh-user", "", "") - fs.BoolVar(&cmd.CopyBinary, "copy-binary", false, "") - fs.StringVar(&cmd.GOOS, "goos", "linux", "") - fs.StringVar(&cmd.GOARCH, "goarch", "amd64", "") - - if err := fs.Parse(args); err != nil { - return err - } - if len(fs.Args()) > 0 { - fmt.Fprintf(cmd.Stderr, "Uknown args: %v\n", strings.Join(fs.Args(), " ")) - return flag.ErrHelp - } - cmd.Hosts = customSplit(hosts, ",") - return nil -} - -// Usage returns the usage message to be printed. -func (cmd *CreateCommand) Usage() string { - return strings.TrimSpace(` -usage: pilosactl create [args] - -Creates a pilosa cluster. Defaults to a 3-node in-process cluster. - -The following flags are allowed: - - -serverN - number of hosts in cluster. Disregarded if 'hosts' is set - - -replicaN - replication factor for cluster - - -hosts - comma separated host:port list. If hosts is set, create will start - pilosa on these pre-existing hosts. The same host may be listed multiple - times with different ports - - -log-file-prefix - output from the started cluster will go into files with - this prefix (one per node) - - -ssh-user - username to use when contacting remote hosts - - -gomaxprocs - when starting a cluster on remote hosts, this will set the value - of GOMAXPROCS. - - -copy-binary - controls whether or not to build and copy pilosa to hosts - - -goos - when using copy-binary, GOOS to use while building binary - - -goarch - when using copy-binary, GOARCH to use while building binary -`) -} - -// Run executes cluster creation. -func (cmd *CreateCommand) Run(ctx context.Context) error { - var clus creator.Cluster - if len(cmd.Hosts) == 0 { - fmt.Fprintf(cmd.Stderr, "create: no hosts specified - creating cluster in-process\n") - clus = &creator.LocalCluster{ - ReplicaN: cmd.ReplicaN, - ServerN: cmd.ServerN, - } - } else { - if cmd.ServerN != 0 { - fmt.Fprintf(cmd.Stderr, "create: hosts were specified, so ignoring serverN\n") - } - clus = &creator.RemoteCluster{ - ClusterHosts: cmd.Hosts, - ReplicaN: cmd.ReplicaN, - SSHUser: cmd.SSHUser, - Stderr: cmd.Stderr, - GoMaxProcs: cmd.GoMaxProcs, - CopyBinary: cmd.CopyBinary, - GOOS: cmd.GOOS, - GOARCH: cmd.GOARCH, - } - - } - - err := clus.Start() - if err != nil { - return fmt.Errorf("starting cluster: %v", err) - } - - c := make(chan os.Signal, 1) - signal.Notify(c, os.Interrupt) - go func() { - for range c { - fmt.Fprintf(cmd.Stderr, "\ncreate: caught signal - shutting down\n") - err := clus.Shutdown() - code := 0 - if err != nil { - code = 1 - } - os.Exit(code) - } - }() - - defer clus.Shutdown() - cmd.FinalHosts = clus.Hosts() - - logReaders := clus.Logs() - if cmd.LogFilePrefix != "" { - cmd.LogFiles = make([]string, len(clus.Hosts())) - } - for i, _ := range clus.Hosts() { - var f io.Writer = cmd.Stderr - var err error - if cmd.LogFilePrefix != "" { - f, err = os.Create(cmd.LogFilePrefix + strconv.Itoa(i)) - if err != nil { - return err - } - cmd.LogFiles[i] = f.(*os.File).Name() - } - - go func(i int, f io.Writer) { - _, err := io.Copy(f, logReaders[i]) - if err != nil { - fmt.Fprintf(cmd.Stderr, "create: error copying cluster logs: '%v'\n", err) - } - }(i, f) - } - - enc := json.NewEncoder(cmd.Stdout) - enc.SetIndent("", " ") - err = enc.Encode(cmd) - if err != nil { - return err - } - fmt.Fprintf(cmd.Stderr, "create: cluster started.\n") - select {} - -} - -// BagentCommand represents a command for running a benchmark agent. A benchmark -// agent runs multiple benchmarks in series in the order that they are specified -// on the command line. -type BagentCommand struct { - // Slice of Benchmarks which will be run serially. - Benchmarks []bench.Benchmark `json:"benchmarks"` - // AgentNum will be passed to each benchmark's Run method so that it can - // parameterize its behavior. - AgentNum int `json:"agent-num"` - - // Enable pretty printing of results, for human consumption. - HumanReadable bool `json:"human-readable"` - - // Slice of pilosa hosts to run the Benchmarks against. - Hosts []string `json:"hosts"` - - Stdin io.Reader `json:"-"` - Stdout io.Writer `json:"-"` - Stderr io.Writer `json:"-"` -} - -// NewBagentCommand returns a new instance of BagentCommand. -func NewBagentCommand(stdin io.Reader, stdout, stderr io.Writer) *BagentCommand { - return &BagentCommand{ - Benchmarks: []bench.Benchmark{}, - Hosts: []string{}, - AgentNum: 0, - HumanReadable: false, - - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - } -} - -// ParseFlags parses command line flags for the BagentCommand. First the command -// wide flags `hosts` and `agent-num` are parsed. The rest of the flags should be -// a series of subcommands along with their flags. ParseFlags runs each -// subcommand's `ConsumeFlags` method which parses the flags for that command -// and returns the rest of the argument slice which should contain further -// subcommands. -func (cmd *BagentCommand) ParseFlags(args []string) error { - fs := flag.NewFlagSet("pilosactl", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - - var pilosaHosts string - fs.StringVar(&pilosaHosts, "hosts", "localhost:15000", "") - fs.IntVar(&cmd.AgentNum, "agent-num", 0, "") - fs.BoolVar(&cmd.HumanReadable, "human", true, "") - - if err := fs.Parse(args); err != nil { - return err - } - remArgs := fs.Args() - if len(remArgs) == 0 { - return flag.ErrHelp - } - for len(remArgs) > 0 { - var bm bench.Command - var err error - switch remArgs[0] { - case "-help", "-h": - return flag.ErrHelp - case "diagonal-set-bits": - bm = &bench.DiagonalSetBits{} - case "random-set-bits": - bm = &bench.RandomSetBits{} - case "zipf": - bm = &bench.Zipf{} - case "multi-db-set-bits": - bm = &bench.MultiDBSetBits{} - case "random-query": - bm = &bench.RandomQuery{} - case "import": - bm = bench.NewImport(cmd.Stdin, cmd.Stdout, cmd.Stderr) - case "slice-height": - bm = bench.NewSliceHeight(cmd.Stdin, cmd.Stdout, cmd.Stderr) - default: - return fmt.Errorf("Unknown benchmark cmd: %v", remArgs[0]) - } - remArgs, err = bm.ConsumeFlags(remArgs[1:]) - cmd.Benchmarks = append(cmd.Benchmarks, bm) - if err != nil { - if err == flag.ErrHelp { - fmt.Fprintln(cmd.Stderr, bm.Usage()) - return fmt.Errorf("") - } - return fmt.Errorf("BagentCommand.ParseFlags: %v", err) - } - } - cmd.Hosts = customSplit(pilosaHosts, ",") - - return nil -} - -// Usage returns the usage message to be printed. -func (cmd *BagentCommand) Usage() string { - return strings.TrimSpace(` -usage: pilosactl bagent [options] ... - -Runs benchmarks against a pilosa cluster. - -The following flags are allowed: - - -hosts ("localhost:15000") - comma separated list of host:port describing all hosts in the cluster - - -agent-num (0) - an integer differentiating this agent from others in the fleet - - -human (true) - boolean to enable human-readable format - - subcommands: - diagonal-set-bits - random-set-bits - zipf - multi-db-set-bits - random-query - import - slice-height -`) -} - -// Run executes the benchmark agent. -func (cmd *BagentCommand) Run(ctx context.Context) error { - sbm := serial(cmd.Benchmarks...) - err := sbm.Init(cmd.Hosts, cmd.AgentNum) - if err != nil { - return fmt.Errorf("in cmd.Run initialization: %v", err) - } - - res := sbm.Run(ctx) - res["agent-num"] = cmd.AgentNum - enc := json.NewEncoder(cmd.Stdout) - if cmd.HumanReadable { - enc.SetIndent("", " ") - res = bench.Prettify(res) - } - err = enc.Encode(res) - if err != nil { - fmt.Fprintln(cmd.Stderr, err) - } - - return nil -} - -// BspawnCommand represents a command for spawning complex benchmarks. This -// includes cluster creation and teardown, agent creation and teardown, running -// multiple benchmarks in series and/or parallel, and collecting all the -// results. -type BspawnCommand struct { - // If PilosaHosts is specified, CreatorArgs is ignored and the existing - // cluster specified here is used. - PilosaHosts []string `json:"pilosa-hosts"` - - // CreateCommand will be used with these arguments to create a cluster - - // the cluster will be used to populate the PilosaHosts field. This - // should include everything that comes after `pilosactl create` - CreatorArgs []string `json:"creator-args"` - - // List of hosts to run agents on. If this is empty, agents will be run - // locally. - AgentHosts []string `json:"agent-hosts"` - - // Makes output human readable - HumanReadable bool `json:"human-readable"` - - // Result destination, ["stdout", "s3"] - Output string `json:"output"` - - // If this is true, build and copy pilosactl binary to agent hosts. - CopyBinary bool `json:"copy-binary"` - GOOS string `json:"goos"` - GOARCH string `json:"goarch"` - - // Benchmarks is a slice of Spawns which specifies all of the bagent - // commands to run. These will all be run in parallel, started on each - // of the agents in a round robin fashion. - Benchmarks []Spawn `json:"benchmarks"` - - SSHUser string `json:"ssh-user"` - - Stdin io.Reader `json:"-"` - Stdout io.Writer `json:"-"` - Stderr io.Writer `json:"-"` -} - -// Spawn represents a bagent command run in parallel across Num agents. The -// bagent command can run multiple Benchmarks serially within itself. -type Spawn struct { - Num int `json:"num"` // number of agents to run - Name string `json:"name"` // Should describe what this Spawn does - Args []string `json:"args"` // everything that comes after `pilosactl bagent [arguments]` -} - -// NewBspawnCommand returns a new instance of BspawnCommand. -func NewBspawnCommand(stdin io.Reader, stdout, stderr io.Writer) *BspawnCommand { - return &BspawnCommand{ - Stdin: stdin, - Stdout: stdout, - Stderr: stderr, - } -} - -// ParseFlags parses command line flags from args. -func (cmd *BspawnCommand) ParseFlags(args []string) error { - fs := flag.NewFlagSet("pilosactl", flag.ContinueOnError) - fs.SetOutput(ioutil.Discard) - creatorHosts := fs.String("creator.hosts", "", "") - creatorLFP := fs.String("creator.log-file-prefix", "", "") - creatorCopyBinary := fs.Bool("creator.copy-binary", false, "") - pilosaHosts := fs.String("pilosa-hosts", "", "") - agentHosts := fs.String("agent-hosts", "", "") - sshUser := fs.String("ssh-user", "", "") - fs.BoolVar(&cmd.HumanReadable, "human", true, "") - fs.StringVar(&cmd.Output, "output", "stdout", "") - fs.BoolVar(&cmd.CopyBinary, "copy-binary", false, "") - fs.StringVar(&cmd.GOOS, "goos", "linux", "") - fs.StringVar(&cmd.GOARCH, "goarch", "amd64", "") - - err := fs.Parse(args) - if err != nil { - return err - } - if len(fs.Args()) != 1 { - return flag.ErrHelp - } - f, err := os.Open(fs.Args()[0]) - if err != nil { - return err - } - dec := json.NewDecoder(f) - err = dec.Decode(cmd) - if err != nil { - return err - } - if *pilosaHosts != "" { - cmd.PilosaHosts = customSplit(*pilosaHosts, ",") - } - if *agentHosts != "" { - cmd.AgentHosts = customSplit(*agentHosts, ",") - } - if *sshUser != "" { - cmd.SSHUser = *sshUser - } - if *pilosaHosts == "" { - cmd.CreatorArgs = []string{"-hosts=" + *creatorHosts, "-log-file-prefix=" + *creatorLFP, "-ssh-user=" + cmd.SSHUser, "-goos=" + cmd.GOOS, "-goarch=" + cmd.GOARCH} - if *creatorCopyBinary { - cmd.CreatorArgs = append(cmd.CreatorArgs, "-copy-binary") - } - } - - return nil -} - -// Usage returns the usage message to be printed. -func (cmd *BspawnCommand) Usage() string { - return strings.TrimSpace(` -usage: pilosactl spawn [flags] - -Benchmark orchestration tool - runs 'create' and potentially multiple instances -of 'bagent' spread across a number of hosts. - -The following flags are allowed and will override the values in the config file: - - -creator.hosts () - hosts argument for pilosactl create - - -creator.log-file-prefix () - log-file-prefix argument for pilosactl create. If empty, log to stderr. - - -creator.copy-binary (false) - pilosactl create should build and copy pilosa binary to cluster - - -pilosa-hosts ([]) - pilosa hosts to run against (will ignore creator args) - - -agent-hosts ("localhost") - hosts to use for benchmark agents - - -ssh-user (current username) - username to use when contacting remote hosts - - -human (true) - toggle human readable output (indented json) - - -output ("stdout") - string to select output destination, "stdout" or "s3" - - -copy-binary (false) - controls whether or not to build and copy pilosactl to agents - - -goos (linux) - when using copy-binary, GOOS to use while building binary - - -goarch (amd64) - when using copy-binary, GOARCH to use while building binary - - -`) -} - -// Run executes the main program execution. -func (cmd *BspawnCommand) Run(ctx context.Context) error { - runUUID := uuid.NewV1() - output := make(map[string]interface{}) - output["run-uuid"] = runUUID.String() - if len(cmd.PilosaHosts) == 0 { - fmt.Fprintln(cmd.Stderr, "bspawn: pilosa-hosts not specified - using create command to build cluster") - r, w := io.Pipe() - createCmd := NewCreateCommand(cmd.Stdin, w, cmd.Stderr) - err := createCmd.ParseFlags(cmd.CreatorArgs) - if err != nil { - return err - } - go func() { - err := createCmd.Run(ctx) - if err != nil { - fmt.Fprintf(cmd.Stderr, "bspawn: cluster creation error while spawning: %v\n", err) - } - }() - clus := &CreateCommand{} - dec := json.NewDecoder(r) - err = dec.Decode(clus) - if err != nil { - return err - } - cmd.PilosaHosts = clus.FinalHosts - output["cluster"] = clus - } - if len(cmd.AgentHosts) == 0 { - fmt.Fprintln(cmd.Stderr, "bpspawn: no agent-hosts specified; all agents will be spawned on localhost") - cmd.AgentHosts = []string{"localhost"} - } - output["spawn"] = cmd - res, err := cmd.spawnRemote(ctx) - if err != nil { - return err - } - output["results"] = res - - var writer io.Writer - if cmd.Output == "s3" { - writer = bench.NewS3Uploader("benchmarks-pilosa", runUUID.String()+".json") - } else if cmd.Output == "stdout" { - writer = cmd.Stdout - } else { - return fmt.Errorf("invalid bspawn output destination") - } - - enc := json.NewEncoder(writer) - if cmd.HumanReadable { - enc.SetIndent("", " ") - output = bench.Prettify(output) - } - return enc.Encode(output) -} - -func (cmd *BspawnCommand) spawnRemote(ctx context.Context) (map[string]interface{}, error) { - agentIdx := 0 - agentFleet, err := pssh.NewFleet(cmd.AgentHosts, cmd.SSHUser, "", cmd.Stderr) - if err != nil { - return nil, err - } - - cmdName := "pilosactl" - if cmd.CopyBinary { - cmdName = "/tmp/pilosactl" + strconv.Itoa(rand.Int()) - fmt.Fprintf(cmd.Stderr, "bspawn: building pilosactl binary with GOOS=%v and GOARCH=%v to copy to agents at %v\n", cmd.GOOS, cmd.GOARCH, cmdName) - pkg := "github.com/pilosa/pilosa/cmd/pilosactl" - bin, err := build.Binary(pkg, cmd.GOOS, cmd.GOARCH) - if err != nil { - return nil, err - } - err = agentFleet.WriteFile(cmdName, "+x", bin) - if err != nil { - return nil, err - } - } - - sessions := make([]*ssh.Session, 0) - results := make(map[string]interface{}) - resLock := sync.Mutex{} - wg := sync.WaitGroup{} - fmt.Fprintln(cmd.Stderr, "bspawn: running benchmarks") - for _, sp := range cmd.Benchmarks { - results[sp.Name] = make(map[int]interface{}) - for i := 0; i < sp.Num; i++ { - agentIdx %= len(cmd.AgentHosts) - sess, err := agentFleet[cmd.AgentHosts[agentIdx]].NewSession() - if err != nil { - return nil, err - } - agentIdx += 1 - sessions = append(sessions, sess) - stdout, err := sess.StdoutPipe() - if err != nil { - return nil, err - } - wg.Add(1) - go func(stdout io.Reader, name string, num int) { - defer wg.Done() - dec := json.NewDecoder(stdout) - var v interface{} - err := dec.Decode(&v) - if err != nil { - fmt.Fprintf(cmd.Stderr, "error decoding json: %v, spawn: %v\n", err, name) - } - resLock.Lock() - results[name].(map[int]interface{})[num] = v - resLock.Unlock() - }(stdout, sp.Name, i) - sess.Stderr = cmd.Stderr - err = sess.Start(cmdName + " bagent -agent-num=" + strconv.Itoa(i) + " -hosts=" + strings.Join(cmd.PilosaHosts, ",") + " " + strings.Join(sp.Args, " ")) - if err != nil { - return nil, err - } - } - } - - for _, sess := range sessions { - err = sess.Wait() - if err != nil { - return nil, fmt.Errorf("error waiting for remote bagent: %v", err) - } - } - wg.Wait() - return results, nil -} - -type serialBenchmark struct { - benchmarkers []bench.Benchmark -} - -// Init calls Init for each benchmark. If there are any errors, it will return a -// non-nil error value. -func (sb *serialBenchmark) Init(hosts []string, agentNum int) error { - errors := make([]error, len(sb.benchmarkers)) - hadErr := false - for i, b := range sb.benchmarkers { - errors[i] = b.Init(hosts, agentNum) - if errors[i] != nil { - hadErr = true - } - } - if hadErr { - return fmt.Errorf("Had errs in serialBenchmark.Init: %v", errors) - } - return nil -} - -// Run runs the serial benchmark and returns it's results in a nested map - the -// top level keys are the indices of each benchmark in the list of benchmarks, -// and the values are the results of each benchmark's Run method. -func (sb *serialBenchmark) Run(ctx context.Context) map[string]interface{} { - benchmarks := make([]map[string]interface{}, len(sb.benchmarkers)) - results := map[string]interface{}{"benchmarks": benchmarks} - - total_start := time.Now() - for i, b := range sb.benchmarkers { - start := time.Now() - output := b.Run(ctx) - if _, ok := output["runtime"]; ok { - panic(fmt.Sprintf("Benchmark %v added 'runtime' to its results", b)) - } - output["runtime"] = time.Now().Sub(start) - ret := map[string]interface{}{"output": output, "metadata": b} - benchmarks[i] = ret - } - results["total_runtime"] = time.Now().Sub(total_start) - return results -} - -// serial takes a variable number of Benchmarks and returns a Benchmark -// which combines then and will run each serially. -func serial(bs ...bench.Benchmark) bench.Benchmark { - return &serialBenchmark{ - benchmarkers: bs, - } -} - // readCSVRow reads a bitmap/profile pair from a CSV row. func readCSVRow(r *csv.Reader) (bitmapID, profileID uint64, timestamp int64, err error) { // Read CSV row. @@ -1709,12 +1024,5 @@ func readCSVRow(r *csv.Reader) (bitmapID, profileID uint64, timestamp int64, err return bitmapID, profileID, timestamp, nil } -func customSplit(s string, sep string) []string { - if s == "" { - return []string{} - } - return strings.Split(s, sep) -} - // errBlank indicates a blank row in a CSV file. var errBlank = errors.New("blank row") diff --git a/cmd/pilosactl/multidbspawn.json b/cmd/pilosactl/multidbspawn.json deleted file mode 100644 index e1c1a6759..000000000 --- a/cmd/pilosactl/multidbspawn.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "benchmarks": [ - { - "num": 3, - "args": ["multi-db-set-bits", "-iterations", "30000", "-client-type", "round_robin"] - } - ] -} diff --git a/cmd/pilosactl/randSetAndQuery.json b/cmd/pilosactl/randSetAndQuery.json deleted file mode 100644 index 92a95e038..000000000 --- a/cmd/pilosactl/randSetAndQuery.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "benchmarks": [ - { - "num": 3, - "args": [ - "random-set-bits", "-iterations", "30000", "-profile-id-range", "1000000", "-bitmap-id-range", "1000000", "-seed", "2345", "-client-type", "round_robin", "-db", "randsetandquery", - "random-query", "-iterations", "1000", "-bitmap-id-range", "1000000", "-seed", "1239", "-dbs", "randsetandquery" - ] - } - ] -} diff --git a/cmd/pilosactl/randspawn.json b/cmd/pilosactl/randspawn.json deleted file mode 100644 index 154cca521..000000000 --- a/cmd/pilosactl/randspawn.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "benchmarks": [ - { - "num": 3, - "args": ["random-set-bits", "-iterations", "30000", "-profile-id-range", "1000000", "-bitmap-id-range", "1000000", "-seed", "2345", "-client-type", "round_robin"] - } - ] -} diff --git a/cmd/pilosactl/slice-height.json b/cmd/pilosactl/slice-height.json deleted file mode 100644 index ac77e501b..000000000 --- a/cmd/pilosactl/slice-height.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "benchmarks": [ - { - "num": 1, - "args": ["-human", "slice-height", "-max-time", "1", "-max-bits-per-map", "100"] - } - ] -} diff --git a/cmd/pilosactl/spawn.json b/cmd/pilosactl/spawn.json deleted file mode 100644 index d1c37e2b3..000000000 --- a/cmd/pilosactl/spawn.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "benchmarks": [ - { - "num": 3, - "name": "set-diags", - "args": ["diagonal-set-bits", "-iterations", "30000", "-client-type", "round_robin"] - }, - { - "num": 2, - "name": "rand-plus-zipf", - "args": ["random-set-bits", "-iterations", "20000", "zipf", "-iterations", "100"] - } - ] -} diff --git a/cmd/pilosactl/zipfspawn.json b/cmd/pilosactl/zipfspawn.json deleted file mode 100644 index 7c6770bdf..000000000 --- a/cmd/pilosactl/zipfspawn.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "benchmarks": [ - { - "num": 1, - "args": ["zipf", "-iterations", "10000", "-profile-id-range", "100", "-bitmap-id-range", "100", "-seed", "2345", "-client-type", "round_robin", "-bitmap-exponent", "1.001", "-bitmap-ratio", ".9", "-profile-exponent", "1.001", "-profile-ratio", ".3"] - }, - { - "num": 1, - "args": ["zipf", "-iterations", "10000", "-profile-id-range", "100", "-bitmap-id-range", "100", "-seed", "2345", "-client-type", "round_robin", "-bitmap-exponent", "1.001", "-bitmap-ratio", ".9", "-profile-exponent", "1.001", "-profile-ratio", ".3", "-operation", "clear"] - } - ] -} diff --git a/creator/creator.go b/creator/creator.go deleted file mode 100644 index a7840dad2..000000000 --- a/creator/creator.go +++ /dev/null @@ -1,11 +0,0 @@ -// creator contains code for standing up pilosa clusters -package creator - -import "io" - -type Cluster interface { - Start() error - Hosts() []string - Shutdown() error - Logs() []io.Reader -} diff --git a/creator/local.go b/creator/local.go deleted file mode 100644 index 4d4b6b8ef..000000000 --- a/creator/local.go +++ /dev/null @@ -1,94 +0,0 @@ -package creator - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "path/filepath" - "strconv" - - "github.com/pilosa/pilosa" -) - -type LocalCluster struct { - ReplicaN int - ServerN int - hosts []string - logs []io.Reader - servers []*pilosa.Server - cluster *pilosa.Cluster - path string -} - -func (localCluster *LocalCluster) Start() error { - BasePort := 19327 - - localCluster.hosts = make([]string, localCluster.ServerN) - localCluster.servers = make([]*pilosa.Server, localCluster.ServerN) - localCluster.logs = make([]io.Reader, localCluster.ServerN) - - path, err := ioutil.TempDir("", "pilosa-bench-") - if err != nil { - return err - } - localCluster.path = path - - // Build cluster configuration. - cluster := pilosa.NewCluster() - cluster.ReplicaN = localCluster.ReplicaN - - for i := 0; i < localCluster.ServerN; i++ { - cluster.Nodes = append(cluster.Nodes, &pilosa.Node{ - Host: fmt.Sprintf("localhost:%d", BasePort+i), - }) - } - localCluster.cluster = cluster - - // Build servers. - for i := range localCluster.servers { - // Make server work directory. - if err := os.MkdirAll(filepath.Join(path, strconv.Itoa(i)), 0777); err != nil { - return err - } - - // Build server. - s := pilosa.NewServer() - s.Host = fmt.Sprintf("localhost:%d", BasePort+i) - s.Cluster = cluster - s.Index.Path = filepath.Join(path, strconv.Itoa(i), "data") - - // Create log stream - localCluster.logs[i], s.LogOutput = io.Pipe() - - localCluster.servers[i] = s - } - - // Open all servers. - for i, s := range localCluster.servers { - if err := s.Open(); err != nil { - return err - } - localCluster.hosts[i] = s.Host - } - - return nil -} - -func (c *LocalCluster) Hosts() []string { return c.hosts } -func (c *LocalCluster) Logs() []io.Reader { return c.logs } -func (c *LocalCluster) Shutdown() error { - errs := "" - for _, s := range c.servers { - if err := s.Close(); err != nil { - errs = errs + err.Error() + "; " - } - } - if err := os.RemoveAll(c.path); err != nil { - errs = errs + err.Error() + ";" - } - if errs != "" { - return fmt.Errorf(errs) - } - return nil -} diff --git a/creator/remote.go b/creator/remote.go deleted file mode 100644 index ebd3a5bcf..000000000 --- a/creator/remote.go +++ /dev/null @@ -1,187 +0,0 @@ -package creator - -import ( - "fmt" - "io" - "math/rand" - "net" - "strconv" - "sync" - "time" - - "github.com/BurntSushi/toml" - "github.com/pilosa/pilosa" - "github.com/pilosa/pilosa/build" - pssh "github.com/pilosa/pilosa/ssh" - "golang.org/x/crypto/ssh" -) - -type RemoteCluster struct { - ClusterHosts []string - ReplicaN int - SSHUser string - Keyfile string - Key []byte - GoMaxProcs int - CopyBinary bool - GOOS string - GOARCH string - Stderr io.Writer - wg *sync.WaitGroup - logs []io.Reader - sessions []*ssh.Session - pipeRs []*io.PipeReader - pipeWs []*io.PipeWriter - stdins []io.WriteCloser -} - -// Start creates a configuration for each host in the cluster, copies it to the -// node, and starts the pilosa process on the remote host. -func (c *RemoteCluster) Start() error { - c.logs = make([]io.Reader, 0) - if len(c.ClusterHosts) == 0 { - return fmt.Errorf("no type or hosts specified - cannot continue") - } - - fleet, err := pssh.NewFleet(c.ClusterHosts, c.SSHUser, c.Keyfile, c.Stderr) - if err != nil { - return fmt.Errorf("connecting to cluster hosts: %v", err) - } - cmdName := "pilosa" - if c.CopyBinary { - cmdName = "/tmp/pilosa" + strconv.Itoa(rand.Int()) - fmt.Fprintf(c.Stderr, "create: building pilosa binary with GOOS=%v and GOARCH=%v to copy to hosts at %v", c.GOOS, c.GOARCH, cmdName) - - pkg := "github.com/pilosa/pilosa/cmd/pilosa" - bin, err := build.Binary(pkg, c.GOOS, c.GOARCH) - if err != nil { - return fmt.Errorf("building binary: %v", err) - } - - err = fleet.WriteFile(cmdName, "+x", bin) - if err != nil { - return fmt.Errorf("writing binary to fleet: %v", err) - } - - } - - // build config - conf := pilosa.NewConfigForHosts(c.ClusterHosts) - conf.Cluster.ReplicaN = c.ReplicaN - - // copy config to remote hosts and start pilosa - c.wg = &sync.WaitGroup{} - for _, hostport := range c.ClusterHosts { - - // Set up config for this host - host, port, err := net.SplitHostPort(hostport) - if err != nil { - return fmt.Errorf("splitting hostport: %v", err) - } - conf.Host = hostport - conf.DataDir = "~/.pilosa" + port - - // Get client for host - client, err := fleet.Get(host) - if err != nil { - return fmt.Errorf("connecting to host: %v", err) - } - configname := "pilosa" + port + ".conf" - w, err := client.OpenFile(configname, "") - if err != nil { - return fmt.Errorf("opening remote config file: %v", err) - } - enc := toml.NewEncoder(w) - err = enc.Encode(conf) - if err != nil { - return fmt.Errorf("encoding config: %v", err) - } - err = w.Close() - if err != nil { - return fmt.Errorf("closing config writer: %v", err) - } - - // Start pilosa on remote host - sess, err := client.NewSession() - if err != nil { - return err - } - // Have to request pty in order to be able to kill remote process - // reliably. - modes := ssh.TerminalModes{ - ssh.ISIG: 1, - ssh.ECHO: 0, - } - err = sess.RequestPty("vt100", 40, 80, modes) - if err != nil { - return fmt.Errorf("request pty error: %v", err) - } - pipeR, pipeW := io.Pipe() - sess.Stdout = pipeW - sess.Stderr = pipeW - inpipe, err := sess.StdinPipe() - if err != nil { - return err - } - c.logs = append(c.logs, pipeR) - c.sessions = append(c.sessions, sess) - c.pipeRs = append(c.pipeRs, pipeR) - c.pipeWs = append(c.pipeWs, pipeW) - c.stdins = append(c.stdins, inpipe) - - gomaxprocsString := "" - if c.GoMaxProcs != 0 { - gomaxprocsString = "GOMAXPROCS=" + strconv.Itoa(c.GoMaxProcs) + " " - } - - err = sess.Start(gomaxprocsString + cmdName + " -config " + configname) - if err != nil { - return err - } - c.wg.Add(1) - go func() { - defer c.wg.Done() - err = sess.Wait() - if err != nil { - fmt.Fprintf(c.Stderr, "problem with remote pilosa process: %v\n", err) - } - }() - } - return nil -} - -func (c *RemoteCluster) Hosts() []string { return c.ClusterHosts } -func (c *RemoteCluster) Logs() []io.Reader { return c.logs } -func (c *RemoteCluster) Shutdown() error { - for i, sess := range c.sessions { - var err error - _, err = c.stdins[i].Write([]byte{3}) // Send Control C - if err != nil { - fmt.Fprintf(c.Stderr, "Error write-signaling remote process: %v\n", err) - } - // signaling isn't supported by many ssh servers - hence the hack above - err = sess.Signal(ssh.SIGINT) - if err != nil { - fmt.Fprintf(c.Stderr, "Error signaling remote process: %v\n", err) - } - } - - done := make(chan struct{}, 1) - go func() { - c.wg.Wait() - close(done) - }() - select { - case <-done: - return nil - case <-time.After(time.Second * 5): - for _, sess := range c.sessions { - err := sess.Close() - if err != nil { - fmt.Fprintf(c.Stderr, "Error closing remote session: %v\n", err) - } - } - return fmt.Errorf("timed out waiting for remote processes to exit") - } - -}