github.com/GuanceCloud/cliutils@v1.1.21/diskcache/put_test.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  package diskcache
     7  
     8  import (
     9  	"errors"
    10  	"sync"
    11  	"sync/atomic"
    12  	T "testing"
    13  	"time"
    14  
    15  	"github.com/GuanceCloud/cliutils/metrics"
    16  	"github.com/prometheus/client_golang/prometheus"
    17  	"github.com/stretchr/testify/assert"
    18  	"github.com/stretchr/testify/require"
    19  )
    20  
    21  func BenchmarkNosyncPutGet(b *T.B) {
    22  	reg := prometheus.NewRegistry()
    23  	register(reg)
    24  
    25  	p := b.TempDir()
    26  	c, err := Open(WithPath(p), WithNoSync(true), WithBatchSize(1024*1024*4), WithCapacity(4*1024*1024*1024))
    27  	require.NoError(b, err)
    28  
    29  	_1mb := make([]byte, 1024*1024)
    30  	_1kb := make([]byte, 1024)
    31  	_512kb := make([]byte, 1024*512)
    32  
    33  	b.Run(`put-1mb`, func(b *T.B) {
    34  		for i := 0; i < b.N; i++ {
    35  			c.Put(_1mb)
    36  		}
    37  	})
    38  
    39  	b.Run(`put-1kb`, func(b *T.B) {
    40  		for i := 0; i < b.N; i++ {
    41  			c.Put(_1kb)
    42  		}
    43  	})
    44  
    45  	b.Run(`put-512kb`, func(b *T.B) {
    46  		for i := 0; i < b.N; i++ {
    47  			c.Put(_512kb)
    48  		}
    49  	})
    50  
    51  	b.Run(`get`, func(b *T.B) {
    52  		for i := 0; i < b.N; i++ {
    53  			c.Get(func(_ []byte) error { return nil })
    54  		}
    55  	})
    56  
    57  	mfs, err := reg.Gather()
    58  	require.NoError(b, err)
    59  	b.Logf("\n%s", metrics.MetricFamily2Text(mfs))
    60  
    61  	b.Cleanup(func() {
    62  		assert.NoError(b, c.Close())
    63  		ResetMetrics()
    64  	})
    65  }
    66  
    67  func BenchmarkPutGet(b *T.B) {
    68  	reg := prometheus.NewRegistry()
    69  	register(reg)
    70  	p := b.TempDir()
    71  	c, err := Open(WithPath(p), WithBatchSize(1024*1024*4), WithCapacity(4*1024*1024*1024))
    72  	require.NoError(b, err)
    73  
    74  	_1mb := make([]byte, 1024*1024)
    75  	_1kb := make([]byte, 1024)
    76  	_512kb := make([]byte, 1024*512)
    77  
    78  	b.Run(`put-1mb`, func(b *T.B) {
    79  		for i := 0; i < b.N; i++ {
    80  			c.Put(_1mb)
    81  		}
    82  	})
    83  
    84  	b.Run(`put-1kb`, func(b *T.B) {
    85  		for i := 0; i < b.N; i++ {
    86  			c.Put(_1kb)
    87  		}
    88  	})
    89  
    90  	b.Run(`put-512kb`, func(b *T.B) {
    91  		for i := 0; i < b.N; i++ {
    92  			c.Put(_512kb)
    93  		}
    94  	})
    95  
    96  	b.Run(`get`, func(b *T.B) {
    97  		for i := 0; i < b.N; i++ {
    98  			c.Get(func(_ []byte) error { return nil })
    99  		}
   100  	})
   101  
   102  	mfs, err := reg.Gather()
   103  	require.NoError(b, err)
   104  
   105  	b.Logf("\n%s", metrics.MetricFamily2Text(mfs))
   106  
   107  	b.Cleanup(func() {
   108  		assert.NoError(b, c.Close())
   109  		ResetMetrics()
   110  	})
   111  }
   112  
   113  func TestConcurrentPutGet(t *T.T) {
   114  	var (
   115  		p      = t.TempDir()
   116  		mb     = int64(1024 * 1024)
   117  		sample = make([]byte, 5*7351)
   118  		eof    = 0
   119  		reg    = prometheus.NewRegistry()
   120  	)
   121  
   122  	register(reg)
   123  
   124  	c, err := Open(WithPath(p), WithBatchSize(4*mb), WithCapacity(128*mb))
   125  	assert.NoError(t, err)
   126  
   127  	defer c.Close()
   128  
   129  	wg := sync.WaitGroup{}
   130  	concurrency := 4
   131  
   132  	fnPut := func(idx int) {
   133  		defer wg.Done()
   134  		nput := 0
   135  		exceed100ms := 0
   136  
   137  		for {
   138  			start := time.Now()
   139  			assert.NoError(t, c.Put(sample))
   140  
   141  			cost := time.Since(start)
   142  			if cost > 100*time.Millisecond {
   143  				exceed100ms++
   144  			}
   145  
   146  			nput++
   147  
   148  			if nput > 1000 {
   149  				t.Logf("[%d] Put exit", idx)
   150  				return
   151  			}
   152  		}
   153  	}
   154  
   155  	wg.Add(concurrency)
   156  	for i := 0; i < concurrency; i++ {
   157  		go fnPut(i)
   158  	}
   159  
   160  	fnGet := func(idx int) {
   161  		defer wg.Done()
   162  		nget := 0
   163  		readBytes := 0
   164  		exceed100ms := 0
   165  
   166  		for {
   167  			nget++
   168  			start := time.Now()
   169  			if err := c.Get(func(x []byte) error {
   170  				assert.Equal(t, sample, x)
   171  				readBytes += len(x)
   172  
   173  				cost := time.Since(start)
   174  				if cost > 100*time.Millisecond {
   175  					exceed100ms++
   176  				}
   177  
   178  				return nil
   179  			}); err != nil {
   180  				if errors.Is(err, ErrEOF) {
   181  					time.Sleep(time.Second)
   182  					eof++
   183  					if eof >= 10 {
   184  						break
   185  					}
   186  				} else {
   187  					t.Logf("[%d]: %s", idx, err)
   188  					time.Sleep(time.Second)
   189  				}
   190  			} else {
   191  				eof = 0 // reset eof if Get ok
   192  			}
   193  		}
   194  	}
   195  
   196  	wg.Add(concurrency)
   197  	for i := 0; i < concurrency; i++ {
   198  		go fnGet(i)
   199  	}
   200  
   201  	wg.Wait()
   202  
   203  	mfs, err := reg.Gather()
   204  	require.NoError(t, err)
   205  	t.Logf("\n%s", metrics.MetricFamily2Text(mfs))
   206  
   207  	t.Cleanup(func() {
   208  		assert.NoError(t, c.Close())
   209  		ResetMetrics()
   210  	})
   211  }
   212  
   213  func TestPutOnCapacityReached(t *T.T) {
   214  	t.Run(`reach-capacity-single-put`, func(t *T.T) {
   215  		var (
   216  			mb       = int64(1024 * 1024)
   217  			p        = t.TempDir()
   218  			capacity = 32 * mb
   219  			large    = make([]byte, mb)
   220  			small    = make([]byte, 1024*3)
   221  			maxPut   = 4 * capacity
   222  		)
   223  
   224  		reg := prometheus.NewRegistry()
   225  		register(reg)
   226  
   227  		t.Logf("path: %s", p)
   228  
   229  		c, err := Open(WithPath(p), WithCapacity(capacity), WithBatchSize(4*mb))
   230  		assert.NoError(t, err)
   231  
   232  		putBytes := 0
   233  
   234  		n := 0
   235  		for {
   236  			switch n % 2 {
   237  			case 0:
   238  				c.Put(small)
   239  				putBytes += len(small)
   240  			case 1:
   241  				c.Put(large)
   242  				putBytes += len(large)
   243  			}
   244  			n++
   245  
   246  			if int64(putBytes) > maxPut {
   247  				break
   248  			}
   249  		}
   250  
   251  		mfs, err := reg.Gather()
   252  		require.NoError(t, err)
   253  
   254  		m := metrics.GetMetricOnLabels(mfs,
   255  			"diskcache_dropped_total",
   256  			c.path,
   257  			reasonExceedCapacity)
   258  
   259  		require.NotNil(t, m, "got metrics:\n%s", metrics.MetricFamily2Text(mfs))
   260  		assert.True(t, m.GetCounter().GetValue() > 0.0)
   261  
   262  		t.Cleanup(func() {
   263  			require.NoError(t, c.Close())
   264  			ResetMetrics()
   265  		})
   266  	})
   267  
   268  	t.Run(`reach-capacity-concurrent-put`, func(t *T.T) {
   269  		var (
   270  			mb       = int64(1024 * 1024)
   271  			p        = t.TempDir()
   272  			capacity = 128 * mb
   273  			large    = make([]byte, mb)
   274  			small    = make([]byte, 1024*3)
   275  			maxPut   = 4 * capacity
   276  			wg       sync.WaitGroup
   277  		)
   278  
   279  		reg := prometheus.NewRegistry()
   280  		register(reg)
   281  
   282  		t.Logf("path: %s", p)
   283  
   284  		c, err := Open(WithPath(p), WithCapacity(capacity), WithBatchSize(4*mb))
   285  		assert.NoError(t, err)
   286  
   287  		total := int64(0)
   288  
   289  		wg.Add(10)
   290  		for i := 0; i < 10; i++ {
   291  			go func() {
   292  				defer wg.Done()
   293  				n := 0
   294  				for {
   295  					switch n % 2 {
   296  					case 0:
   297  						c.Put(small)
   298  						atomic.AddInt64(&total, 1024*3)
   299  					case 1:
   300  						c.Put(large)
   301  						atomic.AddInt64(&total, mb)
   302  					}
   303  					n++
   304  
   305  					if atomic.LoadInt64(&total) > maxPut {
   306  						return
   307  					}
   308  				}
   309  			}()
   310  		}
   311  
   312  		wg.Wait()
   313  
   314  		mfs, err := reg.Gather()
   315  		require.NoError(t, err)
   316  
   317  		m := metrics.GetMetricOnLabels(mfs,
   318  			"diskcache_dropped_total",
   319  			c.path,
   320  			reasonExceedCapacity)
   321  		require.NotNil(t, m, "got metrics:\n%s", metrics.MetricFamily2Text(mfs))
   322  
   323  		assert.True(t, m.GetCounter().GetValue() > 0.0)
   324  
   325  		t.Cleanup(func() {
   326  			assert.NoError(t, c.Close())
   327  			ResetMetrics()
   328  		})
   329  	})
   330  }