github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/storage/index/compaction/compactor_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 compaction
    22  
    23  import (
    24  	"fmt"
    25  	"testing"
    26  
    27  	"github.com/m3db/m3/src/m3ninx/doc"
    28  	"github.com/m3db/m3/src/m3ninx/index/segment"
    29  	"github.com/m3db/m3/src/m3ninx/index/segment/builder"
    30  	"github.com/m3db/m3/src/m3ninx/index/segment/fst"
    31  	"github.com/m3db/m3/src/m3ninx/index/segment/mem"
    32  	"github.com/m3db/m3/src/x/mmap"
    33  	"github.com/m3db/m3/src/x/pool"
    34  
    35  	"github.com/stretchr/testify/require"
    36  )
    37  
    38  var (
    39  	testFSTSegmentOptions     = fst.NewOptions()
    40  	testMemSegmentOptions     = mem.NewOptions()
    41  	testBuilderSegmentOptions = builder.NewOptions()
    42  
    43  	testDocuments = []doc.Metadata{
    44  		{
    45  			ID: []byte("one"),
    46  			Fields: []doc.Field{
    47  				{
    48  					Name:  []byte("fruit"),
    49  					Value: []byte("banana"),
    50  				},
    51  				{
    52  					Name:  []byte("color"),
    53  					Value: []byte("yellow"),
    54  				},
    55  			},
    56  		},
    57  		{
    58  			ID: []byte("two"),
    59  			Fields: []doc.Field{
    60  				{
    61  					Name:  []byte("fruit"),
    62  					Value: []byte("apple"),
    63  				},
    64  				{
    65  					Name:  []byte("color"),
    66  					Value: []byte("red"),
    67  				},
    68  			},
    69  		},
    70  	}
    71  
    72  	testMetadataMaxBatch = 8
    73  	testMetadataPool     = doc.NewMetadataArrayPool(doc.MetadataArrayPoolOpts{
    74  		Options:  pool.NewObjectPoolOptions().SetSize(1),
    75  		Capacity: testMetadataMaxBatch,
    76  	})
    77  )
    78  
    79  func init() {
    80  	testMetadataPool.Init()
    81  }
    82  
    83  func TestCompactorSingleMutableSegment(t *testing.T) {
    84  	seg, err := mem.NewSegment(testMemSegmentOptions)
    85  	require.NoError(t, err)
    86  
    87  	_, err = seg.Insert(testDocuments[0])
    88  	require.NoError(t, err)
    89  
    90  	_, err = seg.Insert(testDocuments[1])
    91  	require.NoError(t, err)
    92  
    93  	compactor, err := NewCompactor(testMetadataPool, testMetadataMaxBatch,
    94  		testBuilderSegmentOptions, testFSTSegmentOptions, CompactorOptions{})
    95  	require.NoError(t, err)
    96  
    97  	result, err := compactor.Compact([]segment.Segment{
    98  		mustSeal(t, seg),
    99  	}, nil, mmap.ReporterOptions{})
   100  	require.NoError(t, err)
   101  
   102  	assertContents(t, result.Compacted, testDocuments)
   103  
   104  	require.NoError(t, compactor.Close())
   105  }
   106  
   107  func TestCompactorSingleMutableSegmentWithMmapDocsData(t *testing.T) {
   108  	seg, err := mem.NewSegment(testMemSegmentOptions)
   109  	require.NoError(t, err)
   110  
   111  	_, err = seg.Insert(testDocuments[0])
   112  	require.NoError(t, err)
   113  
   114  	_, err = seg.Insert(testDocuments[1])
   115  	require.NoError(t, err)
   116  
   117  	compactor, err := NewCompactor(testMetadataPool, testMetadataMaxBatch,
   118  		testBuilderSegmentOptions, testFSTSegmentOptions, CompactorOptions{
   119  			MmapDocsData: true,
   120  		})
   121  	require.NoError(t, err)
   122  
   123  	result, err := compactor.Compact([]segment.Segment{
   124  		mustSeal(t, seg),
   125  	}, nil, mmap.ReporterOptions{})
   126  	require.NoError(t, err)
   127  
   128  	assertContents(t, result.Compacted, testDocuments)
   129  
   130  	require.NoError(t, compactor.Close())
   131  }
   132  
   133  func TestCompactorManySegments(t *testing.T) {
   134  	seg1, err := mem.NewSegment(testMemSegmentOptions)
   135  	require.NoError(t, err)
   136  
   137  	_, err = seg1.Insert(testDocuments[0])
   138  	require.NoError(t, err)
   139  
   140  	seg2, err := mem.NewSegment(testMemSegmentOptions)
   141  	require.NoError(t, err)
   142  
   143  	_, err = seg2.Insert(testDocuments[1])
   144  	require.NoError(t, err)
   145  
   146  	compactor, err := NewCompactor(testMetadataPool, testMetadataMaxBatch,
   147  		testBuilderSegmentOptions, testFSTSegmentOptions, CompactorOptions{})
   148  	require.NoError(t, err)
   149  
   150  	result, err := compactor.Compact([]segment.Segment{
   151  		mustSeal(t, seg1),
   152  		mustSeal(t, seg2),
   153  	}, nil, mmap.ReporterOptions{})
   154  	require.NoError(t, err)
   155  
   156  	assertContents(t, result.Compacted, testDocuments)
   157  
   158  	require.NoError(t, compactor.Close())
   159  }
   160  
   161  func TestCompactorCompactDuplicateIDsNoError(t *testing.T) {
   162  	seg1, err := mem.NewSegment(testMemSegmentOptions)
   163  	require.NoError(t, err)
   164  
   165  	_, err = seg1.Insert(testDocuments[0])
   166  	require.NoError(t, err)
   167  
   168  	seg2, err := mem.NewSegment(testMemSegmentOptions)
   169  	require.NoError(t, err)
   170  
   171  	_, err = seg2.Insert(testDocuments[0])
   172  	require.NoError(t, err)
   173  
   174  	_, err = seg2.Insert(testDocuments[1])
   175  	require.NoError(t, err)
   176  
   177  	compactor, err := NewCompactor(testMetadataPool, testMetadataMaxBatch,
   178  		testBuilderSegmentOptions, testFSTSegmentOptions, CompactorOptions{})
   179  	require.NoError(t, err)
   180  
   181  	result, err := compactor.Compact([]segment.Segment{
   182  		mustSeal(t, seg1),
   183  		mustSeal(t, seg2),
   184  	}, nil, mmap.ReporterOptions{})
   185  	require.NoError(t, err)
   186  
   187  	assertContents(t, result.Compacted, testDocuments)
   188  
   189  	require.NoError(t, compactor.Close())
   190  }
   191  
   192  func assertContents(t *testing.T, seg segment.Segment, docs []doc.Metadata) {
   193  	// Ensure has contents
   194  	require.Equal(t, int64(len(docs)), seg.Size())
   195  	reader, err := seg.Reader()
   196  	require.NoError(t, err)
   197  
   198  	iter, err := reader.AllDocs()
   199  	require.NoError(t, err)
   200  
   201  	foundDocIDs := make(map[string]int)
   202  	for iter.Next() {
   203  		doc := iter.Current()
   204  
   205  		found := false
   206  		for _, testDoc := range docs {
   207  			if string(doc.ID) == string(testDoc.ID) {
   208  				found = true
   209  				foundDocIDs[string(doc.ID)]++
   210  				break
   211  			}
   212  		}
   213  		require.True(t, found,
   214  			fmt.Sprintf("doc_id=\"%s\" not found", string(doc.ID)))
   215  	}
   216  	require.Equal(t, len(docs), len(foundDocIDs))
   217  	for _, count := range foundDocIDs {
   218  		require.Equal(t, 1, count)
   219  	}
   220  
   221  	require.NoError(t, iter.Err())
   222  	require.NoError(t, iter.Close())
   223  }
   224  
   225  func mustSeal(t *testing.T, seg segment.MutableSegment) segment.MutableSegment {
   226  	require.NoError(t, seg.Seal())
   227  	return seg
   228  }