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