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