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

     1  // Copyright (c) 2020  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 bootstrap
    22  
    23  import (
    24  	"io/ioutil"
    25  	"os"
    26  	"sort"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/m3db/m3/src/dbnode/digest"
    31  	"github.com/m3db/m3/src/dbnode/namespace"
    32  	"github.com/m3db/m3/src/dbnode/persist"
    33  	"github.com/m3db/m3/src/dbnode/persist/fs"
    34  	"github.com/m3db/m3/src/dbnode/retention"
    35  	"github.com/m3db/m3/src/x/checked"
    36  	"github.com/m3db/m3/src/x/ident"
    37  	xtime "github.com/m3db/m3/src/x/time"
    38  
    39  	"github.com/stretchr/testify/require"
    40  )
    41  
    42  var (
    43  	testBlockSize             = 2 * time.Hour
    44  	testStart                 = xtime.Now().Truncate(testBlockSize)
    45  	testNamespaceIndexOptions = namespace.NewIndexOptions()
    46  	testNamespaceOptions      = namespace.NewOptions()
    47  	testRetentionOptions      = retention.NewOptions()
    48  	testFilesystemOptions     = fs.NewOptions()
    49  )
    50  
    51  func TestCacheReadInfoFiles(t *testing.T) {
    52  	dir := createTempDir(t)
    53  	defer os.RemoveAll(dir)
    54  
    55  	md1 := testNamespaceMetadata(t, ident.StringID("ns1"))
    56  	md2 := testNamespaceMetadata(t, ident.StringID("ns2"))
    57  
    58  	fsOpts := testFilesystemOptions.SetFilePathPrefix(dir)
    59  
    60  	writeFilesets(t, md1.ID(), 0, fsOpts)
    61  	writeFilesets(t, md1.ID(), 1, fsOpts)
    62  	writeFilesets(t, md2.ID(), 0, fsOpts)
    63  	writeFilesets(t, md2.ID(), 1, fsOpts)
    64  
    65  	opts := NewCacheOptions().
    66  		SetFilesystemOptions(fsOpts).
    67  		SetInstrumentOptions(fsOpts.InstrumentOptions()).
    68  		SetNamespaceDetails([]NamespaceDetails{
    69  			{
    70  				Namespace: md1,
    71  				Shards:    []uint32{0, 1},
    72  			},
    73  			{
    74  				Namespace: md2,
    75  				Shards:    []uint32{0, 1},
    76  			},
    77  		})
    78  	cache, err := NewCache(opts)
    79  	require.NoError(t, err)
    80  
    81  	infoFilesByNamespace := cache.ReadInfoFiles()
    82  	require.NotEmpty(t, infoFilesByNamespace)
    83  
    84  	// Ensure we have two namespaces.
    85  	require.Equal(t, 2, len(infoFilesByNamespace))
    86  
    87  	// Ensure we have two shards.
    88  	filesByShard, err := cache.InfoFilesForNamespace(md1)
    89  	require.NoError(t, err)
    90  	require.Equal(t, 2, len(filesByShard))
    91  
    92  	filesByShard, err = cache.InfoFilesForNamespace(md2)
    93  	require.NoError(t, err)
    94  	require.Equal(t, 2, len(filesByShard))
    95  
    96  	// Ensure each shard has three info files (one for each fileset written).
    97  	for shard := uint32(0); shard < 2; shard++ {
    98  		infoFiles, err := cache.InfoFilesForShard(md1, shard)
    99  		require.NoError(t, err)
   100  		require.Equal(t, 3, len(infoFiles))
   101  
   102  		infoFiles, err = cache.InfoFilesForShard(md2, shard)
   103  		require.NoError(t, err)
   104  		require.Equal(t, 3, len(infoFiles))
   105  	}
   106  }
   107  
   108  func TestCacheReadInfoFilesInvariantViolation(t *testing.T) {
   109  	dir := createTempDir(t)
   110  	defer os.RemoveAll(dir)
   111  
   112  	md1 := testNamespaceMetadata(t, ident.StringID("ns1"))
   113  	md2 := testNamespaceMetadata(t, ident.StringID("ns2"))
   114  
   115  	fsOpts := testFilesystemOptions.SetFilePathPrefix(dir)
   116  	writeFilesets(t, md1.ID(), 0, fsOpts)
   117  
   118  	opts := NewCacheOptions().
   119  		SetFilesystemOptions(fsOpts).
   120  		SetInstrumentOptions(fsOpts.InstrumentOptions()).
   121  		SetNamespaceDetails([]NamespaceDetails{
   122  			{
   123  				Namespace: md1,
   124  				Shards:    []uint32{0, 1},
   125  			},
   126  		})
   127  	cache, err := NewCache(opts)
   128  	require.NoError(t, err)
   129  
   130  	_, err = cache.InfoFilesForNamespace(md2)
   131  	require.Error(t, err)
   132  
   133  	_, err = cache.InfoFilesForShard(md1, 12)
   134  	require.Error(t, err)
   135  }
   136  
   137  func testNamespaceMetadata(t *testing.T, nsID ident.ID) namespace.Metadata {
   138  	rOpts := testRetentionOptions.SetBlockSize(testBlockSize)
   139  	md, err := namespace.NewMetadata(nsID, testNamespaceOptions.
   140  		SetRetentionOptions(rOpts).
   141  		SetIndexOptions(testNamespaceIndexOptions))
   142  	require.NoError(t, err)
   143  	return md
   144  }
   145  
   146  func createTempDir(t *testing.T) string {
   147  	dir, err := ioutil.TempDir("", "foo")
   148  	require.NoError(t, err)
   149  	return dir
   150  }
   151  
   152  type testSeries struct {
   153  	id   string
   154  	tags map[string]string
   155  	data []byte
   156  }
   157  
   158  func writeFilesets(t *testing.T, namespace ident.ID, shard uint32, fsOpts fs.Options) {
   159  	inputs := []struct {
   160  		start xtime.UnixNano
   161  		id    string
   162  		tags  map[string]string
   163  		data  []byte
   164  	}{
   165  		{testStart, "foo", map[string]string{"n": "0"}, []byte{1, 2, 3}},
   166  		{testStart.Add(10 * time.Hour), "bar", map[string]string{"n": "1"}, []byte{4, 5, 6}},
   167  		{testStart.Add(20 * time.Hour), "baz", nil, []byte{7, 8, 9}},
   168  	}
   169  
   170  	for _, input := range inputs {
   171  		writeTSDBFiles(t, namespace, shard, input.start,
   172  			[]testSeries{{input.id, input.tags, input.data}}, fsOpts)
   173  	}
   174  }
   175  
   176  func writeTSDBFiles(
   177  	t require.TestingT,
   178  	namespace ident.ID,
   179  	shard uint32,
   180  	start xtime.UnixNano,
   181  	series []testSeries,
   182  	opts fs.Options,
   183  ) {
   184  	w, err := fs.NewWriter(opts)
   185  	require.NoError(t, err)
   186  	writerOpts := fs.DataWriterOpenOptions{
   187  		Identifier: fs.FileSetFileIdentifier{
   188  			Namespace:  namespace,
   189  			Shard:      shard,
   190  			BlockStart: start,
   191  		},
   192  		BlockSize: testBlockSize,
   193  	}
   194  	require.NoError(t, w.Open(writerOpts))
   195  
   196  	for _, v := range series {
   197  		bytes := checked.NewBytes(v.data, nil)
   198  		bytes.IncRef()
   199  		metadata := persist.NewMetadataFromIDAndTags(ident.StringID(v.id), sortedTagsFromTagsMap(v.tags),
   200  			persist.MetadataOptions{})
   201  		require.NoError(t, w.Write(metadata, bytes, digest.Checksum(bytes.Bytes())))
   202  		bytes.DecRef()
   203  	}
   204  
   205  	require.NoError(t, w.Close())
   206  }
   207  
   208  func sortedTagsFromTagsMap(tags map[string]string) ident.Tags {
   209  	var (
   210  		seriesTags ident.Tags
   211  		tagNames   []string
   212  	)
   213  	for name := range tags {
   214  		tagNames = append(tagNames, name)
   215  	}
   216  	sort.Strings(tagNames)
   217  	for _, name := range tagNames {
   218  		seriesTags.Append(ident.StringTag(name, tags[name]))
   219  	}
   220  	return seriesTags
   221  }