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 })