github.com/schwarzm/garden-linux@v0.0.0-20150507151835-33bca2147c47/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  	"github.com/cloudfoundry-incubator/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  					Path: "echo",
    82  					Args: []string{"hi"},
    83  				}, garden.ProcessIO{})
    84  				Expect(err).ToNot(HaveOccurred())
    85  
    86  				status, err := process.Wait()
    87  				Expect(err).ToNot(HaveOccurred())
    88  				Expect(status).To(Equal(0))
    89  
    90  				if i == 1 {
    91  					firstGoroutineCount = getGoroutineCount()
    92  					b.RecordValue("first goroutine count", float64(firstGoroutineCount))
    93  				}
    94  
    95  				if i == iterations {
    96  					lastGoroutineCount := getGoroutineCount()
    97  					b.RecordValue("last goroutine count", float64(lastGoroutineCount))
    98  
    99  					Expect(lastGoroutineCount).ToNot(BeNumerically(">", firstGoroutineCount+5))
   100  				}
   101  			}
   102  		}, 1)
   103  	})
   104  
   105  	Describe("repeatedly attaching to a running process", func() {
   106  		var processID uint32
   107  
   108  		BeforeEach(func() {
   109  			process, err := container.Run(garden.ProcessSpec{
   110  				Path: "cat",
   111  			}, garden.ProcessIO{})
   112  			Expect(err).ToNot(HaveOccurred())
   113  
   114  			processID = process.ID()
   115  		})
   116  
   117  		Measure("does not leak goroutines", func(b Benchmarker) {
   118  			for i := 1; i <= iterations; i++ {
   119  				stdoutR, stdoutW := io.Pipe()
   120  				stdinR, stdinW := io.Pipe()
   121  
   122  				_, err := container.Attach(processID, garden.ProcessIO{
   123  					Stdin:  stdinR,
   124  					Stdout: stdoutW,
   125  				})
   126  				Expect(err).ToNot(HaveOccurred())
   127  
   128  				stdinData := fmt.Sprintf("hello %d", i)
   129  
   130  				_, err = stdinW.Write([]byte(stdinData + "\n"))
   131  				Expect(err).ToNot(HaveOccurred())
   132  
   133  				var line []byte
   134  				doneReading := make(chan struct{})
   135  				go func() {
   136  					line, _, err = bufio.NewReader(stdoutR).ReadLine()
   137  					close(doneReading)
   138  				}()
   139  
   140  				Eventually(doneReading).Should(BeClosed())
   141  				Expect(err).ToNot(HaveOccurred())
   142  				Expect(string(line)).To(Equal(stdinData))
   143  
   144  				stdinW.CloseWithError(errors.New("going away now"))
   145  
   146  				if i == 1 {
   147  					firstGoroutineCount = getGoroutineCount()
   148  					b.RecordValue("first goroutine count", float64(firstGoroutineCount))
   149  				}
   150  
   151  				if i == iterations {
   152  					lastGoroutineCount := getGoroutineCount()
   153  					b.RecordValue("last goroutine count", float64(lastGoroutineCount))
   154  
   155  					// TODO - we have a leak more.
   156  					// Expect(lastGoroutineCount).ToNot(BeNumerically(">", firstGoroutineCount+5))
   157  				}
   158  			}
   159  		}, 1)
   160  	})
   161  
   162  	Describe("streaming output from a chatty job", func() {
   163  		streamCounts := []int{0}
   164  
   165  		for i := 1; i <= numStreams; i *= 2 {
   166  			streamCounts = append(streamCounts, i)
   167  		}
   168  
   169  		for _, streams := range streamCounts {
   170  			Context(fmt.Sprintf("with %d streams", streams), func() {
   171  				var started time.Time
   172  				var receivedBytes uint64
   173  
   174  				numToSpawn := streams
   175  
   176  				BeforeEach(func() {
   177  					atomic.StoreUint64(&receivedBytes, 0)
   178  					started = time.Now()
   179  
   180  					byteCounter := &byteCounterWriter{&receivedBytes}
   181  
   182  					spawned := make(chan bool)
   183  
   184  					for j := 0; j < numToSpawn; j++ {
   185  						go func() {
   186  							defer GinkgoRecover()
   187  
   188  							_, err := container.Run(garden.ProcessSpec{
   189  								Path: "cat",
   190  								Args: []string{"/dev/zero"},
   191  							}, garden.ProcessIO{
   192  								Stdout: byteCounter,
   193  							})
   194  							Expect(err).ToNot(HaveOccurred())
   195  
   196  							spawned <- true
   197  						}()
   198  					}
   199  
   200  					for j := 0; j < numToSpawn; j++ {
   201  						<-spawned
   202  					}
   203  				})
   204  
   205  				AfterEach(func() {
   206  					err := client.Destroy(container.Handle())
   207  					Expect(err).ToNot(HaveOccurred())
   208  				})
   209  
   210  				Measure("it should not adversely affect the rest of the API", func(b Benchmarker) {
   211  					var newContainer garden.Container
   212  
   213  					b.Time("creating another container", func() {
   214  						var err error
   215  
   216  						newContainer, err = client.Create(garden.ContainerSpec{})
   217  						Expect(err).ToNot(HaveOccurred())
   218  					})
   219  
   220  					for i := 0; i < repeats; i++ {
   221  						b.Time("getting container info ("+strconv.Itoa(repeats)+"x)", func() {
   222  							_, err := newContainer.Info()
   223  							Expect(err).ToNot(HaveOccurred())
   224  						})
   225  					}
   226  
   227  					for i := 0; i < repeats; i++ {
   228  						b.Time("running a job ("+strconv.Itoa(repeats)+"x)", func() {
   229  							process, err := newContainer.Run(garden.ProcessSpec{Path: "ls"}, garden.ProcessIO{})
   230  							Expect(err).ToNot(HaveOccurred())
   231  
   232  							Expect(process.Wait()).To(Equal(0))
   233  						})
   234  					}
   235  
   236  					b.Time("destroying the container", func() {
   237  						err := client.Destroy(newContainer.Handle())
   238  						Expect(err).ToNot(HaveOccurred())
   239  					})
   240  
   241  					b.RecordValue(
   242  						"received rate (bytes/second)",
   243  						float64(atomic.LoadUint64(&receivedBytes))/float64(time.Since(started)/time.Second),
   244  					)
   245  
   246  					fmt.Println("total time:", time.Since(started))
   247  				}, streamSamples)
   248  			})
   249  		}
   250  	})
   251  })