github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/index/block_bench_test.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package index
    22  
    23  import (
    24  	"fmt"
    25  	"math/rand"
    26  	"os"
    27  	"strings"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/m3db/m3/src/dbnode/namespace"
    32  	"github.com/m3db/m3/src/m3ninx/doc"
    33  	"github.com/m3db/m3/src/x/resource"
    34  	xtime "github.com/m3db/m3/src/x/time"
    35  
    36  	"github.com/golang/mock/gomock"
    37  	"github.com/pkg/profile"
    38  	"github.com/stretchr/testify/require"
    39  )
    40  
    41  func BenchmarkBlockWrite(b *testing.B) {
    42  	ctrl := gomock.NewController(b)
    43  	defer ctrl.Finish()
    44  
    45  	testMD := newTestNSMetadata(b)
    46  	blockSize := time.Hour
    47  
    48  	now := xtime.Now()
    49  	blockStart := now.Truncate(blockSize)
    50  
    51  	bl, err := NewBlock(blockStart, testMD, BlockOptions{},
    52  		namespace.NewRuntimeOptionsManager("foo"), testOpts)
    53  	require.NoError(b, err)
    54  	defer func() {
    55  		require.NoError(b, bl.Close())
    56  	}()
    57  
    58  	var onIndexSeries mockOnIndexSeries
    59  	batch := NewWriteBatch(WriteBatchOptions{
    60  		IndexBlockSize: blockSize,
    61  	})
    62  
    63  	fieldValues := map[string][]string{
    64  		"fruit":     {"apple", "banana", "orange", "watermelon"},
    65  		"vegetable": {"broccoli", "carrot", "celery", "cucumber"},
    66  		"meat":      {"beef", "chicken", "pork", "steak"},
    67  		"cheese":    {"cheddar", "swiss", "brie", "bleu"},
    68  	}
    69  
    70  	for i := 0; i < 4096; i++ {
    71  		fields := make([]doc.Field, 0, len(fieldValues))
    72  		for key, values := range fieldValues {
    73  			fields = append(fields, doc.Field{
    74  				Name:  []byte(key),
    75  				Value: []byte(values[rand.Intn(len(values))]),
    76  			})
    77  		}
    78  		batch.Append(WriteBatchEntry{
    79  			Timestamp:     now,
    80  			OnIndexSeries: onIndexSeries,
    81  			EnqueuedAt:    now.ToTime(),
    82  		}, doc.Metadata{
    83  			ID:     []byte(fmt.Sprintf("doc.%d", i)),
    84  			Fields: fields,
    85  		})
    86  	}
    87  
    88  	if strings.ToLower(os.Getenv("PROFILE_CPU")) == "true" {
    89  		p := profile.Start(profile.CPUProfile)
    90  		defer p.Stop()
    91  	}
    92  
    93  	b.ResetTimer()
    94  
    95  	b.StartTimer()
    96  	for i := 0; i < b.N; i++ {
    97  		// Simulate all documents being pending on consequent
    98  		// write batch calls
    99  		for _, entry := range batch.entries {
   100  			entry.result.Done = false
   101  		}
   102  
   103  		_, err := bl.WriteBatch(batch)
   104  		require.NoError(b, err)
   105  
   106  		// Reset state
   107  		bl.(*block).Lock()
   108  		bl.(*block).mutableSegments.foregroundSegments = nil
   109  		bl.(*block).Unlock()
   110  	}
   111  	b.StopTimer()
   112  }
   113  
   114  // mockOnIndexSeries is a by hand generated struct since using the
   115  // gomock generated ones is really slow so makes them almost
   116  // useless to use in benchmarks
   117  type mockOnIndexSeries struct{}
   118  
   119  var _ doc.OnIndexSeries = mockOnIndexSeries{}
   120  
   121  func (m mockOnIndexSeries) StringID() string               { return "mock" }
   122  func (m mockOnIndexSeries) OnIndexSuccess(xtime.UnixNano)  {}
   123  func (m mockOnIndexSeries) OnIndexFinalize(xtime.UnixNano) {}
   124  func (m mockOnIndexSeries) OnIndexPrepare(xtime.UnixNano)  {}
   125  func (m mockOnIndexSeries) TryReconcileDuplicates()        {}
   126  func (m mockOnIndexSeries) NeedsIndexUpdate(xtime.UnixNano) bool {
   127  	return false
   128  }
   129  func (m mockOnIndexSeries) DecrementReaderWriterCount() {}
   130  func (m mockOnIndexSeries) IfAlreadyIndexedMarkIndexSuccessAndFinalize(xtime.UnixNano) bool {
   131  	return false
   132  }
   133  func (m mockOnIndexSeries) IndexedForBlockStart(xtime.UnixNano) bool                    { return false }
   134  func (m mockOnIndexSeries) IndexedOrAttemptedAny() bool                                 { return false }
   135  func (m mockOnIndexSeries) TryMarkIndexGarbageCollected() bool                          { return false }
   136  func (m mockOnIndexSeries) NeedsIndexGarbageCollected() bool                            { return false }
   137  func (m mockOnIndexSeries) MergeEntryIndexBlockStates(states doc.EntryIndexBlockStates) {}
   138  func (m mockOnIndexSeries) IndexedRange() (xtime.UnixNano, xtime.UnixNano) {
   139  	return 0, 0
   140  }
   141  
   142  func (m mockOnIndexSeries) ReconciledOnIndexSeries() (doc.OnIndexSeries, resource.SimpleCloser, bool) {
   143  	return m, resource.SimpleCloserFn(func() {}), false
   144  }