@cryptotaxi247 / kubo / commits / db7d7ae89

net: sending backpressure test

Juan Batiz-Benet committed Dec 17, 2014 at 19:19 UTC db7d7ae8916083c58317eb830ec2eb2f74817dce
1 file changed +174
net/backpressure/backpressure_test.go
+174
@@ -1,6 +1,9 @@
1 package backpressure_tests
2
3 import (
4 + crand "crypto/rand"
5 + "io"
6 + "math/rand"
7 "testing"
8 "time"
9
@@ -206,3 +209,174 @@ a problem.
209 log.Info("handler backpressure works!")
210 }
211 }
212 +
213 +// TestStBackpressureStreamWrite tests whether streams see proper
214 +// backpressure when writing data over the network streams.
215 +func TestStBackpressureStreamWrite(t *testing.T) {
216 +
217 + // senderWrote signals that the sender wrote bytes to remote.
218 + // the value is the count of bytes written.
219 + senderWrote := make(chan int, 10000)
220 +
221 + // sender signals it's done (errored out)
222 + senderDone := make(chan struct{})
223 +
224 + // writeStats lets us listen to all the writes and return
225 + // how many happened and how much was written
226 + writeStats := func() (int, int) {
227 + writes := 0
228 + bytes := 0
229 + for {
230 + select {
231 + case n := <-senderWrote:
232 + writes++
233 + bytes = bytes + n
234 + default:
235 + log.Debugf("stats: sender wrote %d bytes, %d writes", bytes, writes)
236 + return bytes, writes
237 + }
238 + }
239 + }
240 +
241 + // sender attempts to write as fast as possible, signaling on the
242 + // completion of every write. This makes it possible to see how
243 + // fast it's actually writing. We pair this with a receiver
244 + // that waits for a signal to read.
245 + sender := func(s inet.Stream) {
246 + defer func() {
247 + s.Close()
248 + senderDone <- struct{}{}
249 + }()
250 +
251 + // ready a buffer of random data
252 + buf := make([]byte, 65536)
253 + crand.Read(buf)
254 +
255 + for {
256 + // send a randomly sized subchunk
257 + from := rand.Intn(len(buf) / 2)
258 + to := rand.Intn(len(buf) / 2)
259 + sendbuf := buf[from : from+to]
260 +
261 + n, err := s.Write(sendbuf)
262 + if err != nil {
263 + log.Debug("sender error. exiting:", err)
264 + return
265 + }
266 +
267 + log.Debugf("sender wrote %d bytes", n)
268 + senderWrote <- n
269 + }
270 + }
271 +
272 + // receive a number of bytes from a stream.
273 + // returns the number of bytes written.
274 + receive := func(s inet.Stream, expect int) {
275 + log.Debugf("receiver to read %d bytes", expect)
276 + rbuf := make([]byte, expect)
277 + n, err := io.ReadFull(s, rbuf)
278 + if err != nil {
279 + t.Error("read failed:", err)
280 + }
281 + if expect != n {
282 + t.Error("read len differs: %d != %d", expect, n)
283 + }
284 + }
285 +
286 + // ok let's do it!
287 +
288 + // setup the networks
289 + ctx := context.Background()
290 + n1, err := GenNetwork(ctx)
291 + if err != nil {
292 + t.Fatal(err)
293 + }
294 + n2, err := GenNetwork(ctx)
295 + if err != nil {
296 + t.Fatal(err)
297 + }
298 +
299 + // setup sender handler on 1
300 + n1.SetHandler(inet.ProtocolTesting, sender)
301 +
302 + log.Debugf("dialing %s", n2.ListenAddresses())
303 + if err := n1.DialPeer(ctx, n2.LocalPeer()); err != nil {
304 + t.Fatalf("Failed to dial:", err)
305 + }
306 +
307 + // open a stream, from 2->1, this is our reader
308 + s, err := n2.NewStream(inet.ProtocolTesting, n1.LocalPeer())
309 +
310 + // let's make sure r/w works.
311 + testSenderWrote := func(bytesE int) {
312 + bytesA, writesA := writeStats()
313 + if bytesA != bytesE {
314 + t.Errorf("numbers failed: %d =?= %d bytes, via %d writes", bytesA, bytesE, writesA)
315 + }
316 + }
317 +
318 + // 500ms rounds of lockstep write + drain
319 + roundsStart := time.Now()
320 + roundsTotal := 0
321 + for roundsTotal < (2 << 20) {
322 + // let the sender fill its buffers, it will stop sending.
323 + <-time.After(300 * time.Millisecond)
324 + b, _ := writeStats()
325 + testSenderWrote(0)
326 + testSenderWrote(0)
327 +
328 + // drain it all, wait again
329 + receive(s, b)
330 + roundsTotal = roundsTotal + b
331 + }
332 + roundsTime := time.Now().Sub(roundsStart)
333 +
334 + // now read continously, while we measure stats.
335 + stop := make(chan struct{})
336 + contStart := time.Now()
337 +
338 + go func() {
339 + for {
340 + select {
341 + case <-stop:
342 + return
343 + default:
344 + receive(s, 2<<15)
345 + }
346 + }
347 + }()
348 +
349 + contTotal := 0
350 + for contTotal < (2 << 20) {
351 + n := <-senderWrote
352 + contTotal += n
353 + }
354 + stop <- struct{}{}
355 + contTime := time.Now().Sub(contStart)
356 +
357 + if roundsTime < contTime {
358 + t.Error("continuous should have been faster")
359 + }
360 +
361 + if roundsTotal < contTotal {
362 + t.Error("continuous should have been larger, too!")
363 + }
364 +
365 + <-time.After(300 * time.Millisecond)
366 + writeStats()
367 + testSenderWrote(0)
368 + testSenderWrote(0)
369 +
370 + // this doesn't work :(:
371 + // // now for the sugar on top: let's tear down the receiver. it should
372 + // // exit the sender.
373 + // n1.Close()
374 + // testSenderWrote(0)
375 + // testSenderWrote(0)
376 + // select {
377 + // case <-time.After(2 * time.Second):
378 + // t.Error("receiver shutdown failed to exit sender")
379 + // case <-senderDone:
380 + // log.Info("handler backpressure works!")
381 + // }
382 +}