github.com/geofffranks/garden-linux@v0.0.0-20160715111146-26c893169cfa/integration/measurements/stream_measure_test.go (about)

     1  package measurements_test
     2  
     3  import (
     4  	"bufio"
     5  	"errors"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  	"runtime"
    11  	"strconv"
    12  	"strings"
    13  	"sync/atomic"
    14  	"time"
    15  
    16  	"code.cloudfoundry.org/garden"
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  )
    20  
    21  const (
    22  	iterations    = 0 // e.g. 50
    23  	numStreams    = 0 // e.g. 128
    24  	repeats       = 0 // e.g. 10
    25  	streamSamples = 0 // e.g. 5
    26  )
    27  
    28  type byteCounterWriter struct {
    29  	num *uint64
    30  }
    31  
    32  func (w *byteCounterWriter) Write(d []byte) (int, error) {
    33  	atomic.AddUint64(w.num, uint64(len(d)))
    34  	return len(d), nil
    35  }
    36  
    37  func (w *byteCounterWriter) Close() error {
    38  	return nil
    39  }
    40  
    41  var _ = Describe("The Garden server", func() {
    42  	runtime.GOMAXPROCS(runtime.NumCPU())
    43  
    44  	var container garden.Container
    45  	var firstGoroutineCount uint64
    46  	var debugAddr string
    47  
    48  	BeforeEach(func() {
    49  		firstGoroutineCount = 0
    50  		debugAddr = fmt.Sprintf("0.0.0.0:%d", 15000+GinkgoParallelNode())
    51  		client = startGarden("--debugAddr", debugAddr)
    52  
    53  		var err error
    54  		container, err = client.Create(garden.ContainerSpec{})
    55  		Expect(err).ToNot(HaveOccurred())
    56  	})
    57  
    58  	getGoroutineCount := func(printIt ...bool) uint64 {
    59  		resp, err := http.Get(fmt.Sprintf("http://%s/debug/pprof/goroutine?debug=1", debugAddr))
    60  		Expect(err).ToNot(HaveOccurred())
    61  
    62  		line, _, err := bufio.NewReader(resp.Body).ReadLine()
    63  		Expect(err).ToNot(HaveOccurred())
    64  
    65  		if len(printIt) > 0 && printIt[0] {
    66  			io.Copy(os.Stdout, resp.Body)
    67  		}
    68  
    69  		words := strings.Split(string(line), " ")
    70  
    71  		goroutineCount, err := strconv.ParseUint(words[len(words)-1], 10, 64)
    72  		Expect(err).ToNot(HaveOccurred())
    73  
    74  		return goroutineCount
    75  	}
    76  
    77  	Describe("repeatedly running processes", func() {
    78  		Measure("does not leak goroutines", func(b Benchmarker) {
    79  			for i := 1; i <= iterations; i++ {
    80  				process, err := container.Run(garden.ProcessSpec{
    81  					User: "alice",
    82  					Path: "echo",
    83  					Args: []string{"hi"},
    84  				}, garden.ProcessIO{})
    85  				Expect(err).ToNot(HaveOccurred())
    86  
    87  				status, err := process.Wait()
    88  				Expect(err).ToNot(HaveOccurred())
    89  				Expect(status).To(Equal(0))
    90  
    91  				if i == 1 {
    92  					firstGoroutineCount = getGoroutineCount()
    93  					b.RecordValue("first goroutine count", float64(firstGoroutineCount))
    94  				}
    95  
    96  				if i == iterations {
    97  					lastGoroutineCount := getGoroutineCount()
    98  					b.RecordValue("last goroutine count", float64(lastGoroutineCount))
    99  
   100  					Expect(lastGoroutineCount).ToNot(BeNumerically(">", firstGoroutineCount+5))
   101  				}
   102  			}
   103  		}, 1)
   104  	})
   105  
   106  	Describe("repeatedly attaching to a running process", func() {
   107  		var processID string
   108  
   109  		BeforeEach(func() {
   110  			process, err := container.Run(garden.ProcessSpec{
   111  				User: "alice",
   112  				Path: "cat",
   113  			}, garden.ProcessIO{})
   114  			Expect(err).ToNot(HaveOccurred())
   115  
   116  			processID = process.ID()
   117  		})
   118  
   119  		Measure("does not leak goroutines", func(b Benchmarker) {
   120  			for i := 1; i <= iterations; i++ {
   121  				stdoutR, stdoutW := io.Pipe()
   122  				stdinR, stdinW := io.Pipe()
   123  
   124  				_, err := container.Attach(processID, garden.ProcessIO{
   125  					Stdin:  stdinR,
   126  					Stdout: stdoutW,
   127  				})
   128  				Expect(err).ToNot(HaveOccurred())
   129  
   130  				stdinData := fmt.Sprintf("hello %d", i)
   131  
   132  				_, err = stdinW.Write([]byte(stdinData + "\n"))
   133  				Expect(err).ToNot(HaveOccurred())
   134  
   135  				var line []byte
   136  				doneReading := make(chan struct{})
   137  				go func() {
   138  					line, _, err = bufio.NewReader(stdoutR).ReadLine()
   139  					close(doneReading)
   140  				}()
   141  
   142  				Eventually(doneReading).Should(BeClosed())
   143  				Expect(err).ToNot(HaveOccurred())
   144  				Expect(string(line)).To(Equal(stdinData))
   145  
   146  				stdinW.CloseWithError(errors.New("going away now"))
   147  
   148  				if i == 1 {
   149  					firstGoroutineCount = getGoroutineCount()
   150  					b.RecordValue("first goroutine count", float64(firstGoroutineCount))
   151  				}
   152  
   153  				if i == iterations {
   154  					lastGoroutineCount := getGoroutineCount()
   155  					b.RecordValue("last goroutine count", float64(lastGoroutineCount))
   156  
   157  					// TODO - we have a leak more.
   158  					// Expect(lastGoroutineCount).ToNot(BeNumerically(">", firstGoroutineCount+5))
   159  				}
   160  			}
   161  		}, 1)
   162  	})
   163  
   164  	Describe("streaming output from a chatty job", func() {
   165  		streamCounts := []int{0}
   166  
   167  		for i := 1; i <= numStreams; i *= 2 {
   168  			streamCounts = append(streamCounts, i)
   169  		}
   170  
   171  		for _, streams := range streamCounts {
   172  			Context(fmt.Sprintf("with %d streams", streams), func() {
   173  				var started time.Time
   174  				var receivedBytes uint64
   175  
   176  				numToSpawn := streams
   177  
   178  				BeforeEach(func() {
   179  					atomic.StoreUint64(&receivedBytes, 0)
   180  					started = time.Now()
   181  
   182  					byteCounter := &byteCounterWriter{&receivedBytes}
   183  
   184  					spawned := make(chan bool)
   185  
   186  					for j := 0; j < numToSpawn; j++ {
   187  						go func() {
   188  							defer GinkgoRecover()
   189  
   190  							_, err := container.Run(garden.ProcessSpec{
   191  								User: "alice",
   192  								Path: "cat",
   193  								Args: []string{"/dev/zero"},
   194  							}, garden.ProcessIO{
   195  								Stdout: byteCounter,
   196  							})
   197  							Expect(err).ToNot(HaveOccurred())
   198  
   199  							spawned <- true
   200  						}()
   201  					}
   202  
   203  					for j := 0; j < numToSpawn; j++ {
   204  						<-spawned
   205  					}
   206  				})
   207  
   208  				AfterEach(func() {
   209  					err := client.Destroy(container.Handle())
   210  					Expect(err).ToNot(HaveOccurred())
   211  				})
   212  
   213  				Measure("it should not adversely affect the rest of the API", func(b Benchmarker) {
   214  					var newContainer garden.Container
   215  
   216  					b.Time("creating another container", func() {
   217  						var err error
   218  
   219  						newContainer, err = client.Create(garden.ContainerSpec{})
   220  						Expect(err).ToNot(HaveOccurred())
   221  					})
   222  
   223  					for i := 0; i < repeats; i++ {
   224  						b.Time("getting container info ("+strconv.Itoa(repeats)+"x)", func() {
   225  							_, err := newContainer.Info()
   226  							Expect(err).ToNot(HaveOccurred())
   227  						})
   228  					}
   229  
   230  					for i := 0; i < repeats; i++ {
   231  						b.Time("running a job ("+strconv.Itoa(repeats)+"x)", func() {
   232  							process, err := newContainer.Run(garden.ProcessSpec{
   233  								User: "alice", Path: "ls",
   234  							}, garden.ProcessIO{})
   235  							Expect(err).ToNot(HaveOccurred())
   236  
   237  							Expect(process.Wait()).To(Equal(0))
   238  						})
   239  					}
   240  
   241  					b.Time("destroying the container", func() {
   242  						err := client.Destroy(newContainer.Handle())
   243  						Expect(err).ToNot(HaveOccurred())
   244  					})
   245  
   246  					b.RecordValue(
   247  						"received rate (bytes/second)",
   248  						float64(atomic.LoadUint64(&receivedBytes))/float64(time.Since(started)/time.Second),
   249  					)
   250  
   251  					fmt.Println("total time:", time.Since(started))
   252  				}, streamSamples)
   253  			})
   254  		}
   255  	})
   256  })