Merge branch 'main' from arvados-workbench2.git
[arvados.git] / services / keepstore / bufferpool_test.go
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 package keepstore
6
7 import (
8         "context"
9         "time"
10
11         "git.arvados.org/arvados.git/sdk/go/ctxlog"
12         . "gopkg.in/check.v1"
13 )
14
15 var _ = Suite(&BufferPoolSuite{})
16
17 type BufferPoolSuite struct{}
18
19 // Initialize a default-sized buffer pool for the benefit of test
20 // suites that don't run main().
21 func init() {
22         bufs = newBufferPool(ctxlog.FromContext(context.Background()), 12, BlockSize)
23 }
24
25 // Restore sane default after bufferpool's own tests
26 func (s *BufferPoolSuite) TearDownTest(c *C) {
27         bufs = newBufferPool(ctxlog.FromContext(context.Background()), 12, BlockSize)
28 }
29
30 func (s *BufferPoolSuite) TestBufferPoolBufSize(c *C) {
31         bufs := newBufferPool(ctxlog.TestLogger(c), 2, 10)
32         b1 := bufs.Get(1)
33         bufs.Get(2)
34         bufs.Put(b1)
35         b3 := bufs.Get(3)
36         c.Check(len(b3), Equals, 3)
37 }
38
39 func (s *BufferPoolSuite) TestBufferPoolUnderLimit(c *C) {
40         bufs := newBufferPool(ctxlog.TestLogger(c), 3, 10)
41         b1 := bufs.Get(10)
42         bufs.Get(10)
43         testBufferPoolRace(c, bufs, b1, "Get")
44 }
45
46 func (s *BufferPoolSuite) TestBufferPoolAtLimit(c *C) {
47         bufs := newBufferPool(ctxlog.TestLogger(c), 2, 10)
48         b1 := bufs.Get(10)
49         bufs.Get(10)
50         testBufferPoolRace(c, bufs, b1, "Put")
51 }
52
53 func testBufferPoolRace(c *C, bufs *bufferPool, unused []byte, expectWin string) {
54         race := make(chan string)
55         go func() {
56                 bufs.Get(10)
57                 time.Sleep(time.Millisecond)
58                 race <- "Get"
59         }()
60         go func() {
61                 time.Sleep(10 * time.Millisecond)
62                 bufs.Put(unused)
63                 race <- "Put"
64         }()
65         c.Check(<-race, Equals, expectWin)
66         c.Check(<-race, Not(Equals), expectWin)
67         close(race)
68 }
69
70 func (s *BufferPoolSuite) TestBufferPoolReuse(c *C) {
71         bufs := newBufferPool(ctxlog.TestLogger(c), 2, 10)
72         bufs.Get(10)
73         last := bufs.Get(10)
74         // The buffer pool is allowed to throw away unused buffers
75         // (e.g., during sync.Pool's garbage collection hook, in the
76         // the current implementation). However, if unused buffers are
77         // getting thrown away and reallocated more than {arbitrary
78         // frequency threshold} during a busy loop, it's not acting
79         // much like a buffer pool.
80         allocs := 1000
81         reuses := 0
82         for i := 0; i < allocs; i++ {
83                 bufs.Put(last)
84                 next := bufs.Get(10)
85                 copy(last, []byte("last"))
86                 copy(next, []byte("next"))
87                 if last[0] == 'n' {
88                         reuses++
89                 }
90                 last = next
91         }
92         c.Check(reuses > allocs*95/100, Equals, true)
93 }