github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/integration/dynamic_namespace_add_test.go (about)

     1  // +build integration
     2  
     3  // Copyright (c) 2016 Uber Technologies, Inc.
     4  //
     5  // Permission is hereby granted, free of charge, to any person obtaining a copy
     6  // of this software and associated documentation files (the "Software"), to deal
     7  // in the Software without restriction, including without limitation the rights
     8  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     9  // copies of the Software, and to permit persons to whom the Software is
    10  // furnished to do so, subject to the following conditions:
    11  //
    12  // The above copyright notice and this permission notice shall be included in
    13  // all copies or substantial portions of the Software.
    14  //
    15  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    16  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    17  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    18  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    19  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    20  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    21  // THE SOFTWARE.
    22  
    23  package integration
    24  
    25  import (
    26  	"sync"
    27  	"testing"
    28  	"time"
    29  
    30  	"github.com/m3db/m3/src/cluster/integration/etcd"
    31  	"github.com/m3db/m3/src/dbnode/integration/generate"
    32  	"github.com/m3db/m3/src/dbnode/namespace"
    33  	"github.com/m3db/m3/src/dbnode/storage/block"
    34  	xtime "github.com/m3db/m3/src/x/time"
    35  
    36  	"github.com/golang/mock/gomock"
    37  	"github.com/golang/protobuf/proto"
    38  	"github.com/stretchr/testify/require"
    39  )
    40  
    41  func TestDynamicNamespaceAdd(t *testing.T) {
    42  	if testing.Short() {
    43  		t.SkipNow() // Just skip if we're doing a short run
    44  	}
    45  	ctrl := gomock.NewController(t)
    46  	defer ctrl.Finish()
    47  
    48  	// test options
    49  	testOpts := NewTestOptions(t).
    50  		SetTickMinimumInterval(time.Second)
    51  	require.True(t, len(testOpts.Namespaces()) >= 2)
    52  	ns0 := testOpts.Namespaces()[0]
    53  	ns1 := testOpts.Namespaces()[1]
    54  
    55  	// embedded kv
    56  	embeddedKV, err := etcd.New(etcd.NewOptions())
    57  	require.NoError(t, err)
    58  	defer func() {
    59  		require.NoError(t, embeddedKV.Close())
    60  	}()
    61  	require.NoError(t, embeddedKV.Start())
    62  	csClient, err := embeddedKV.ConfigServiceClient()
    63  	require.NoError(t, err)
    64  	kvStore, err := csClient.KV()
    65  	require.NoError(t, err)
    66  
    67  	// namespace maps
    68  	protoKey := func(nses ...namespace.Metadata) proto.Message {
    69  		nsMap, err := namespace.NewMap(nses)
    70  		require.NoError(t, err)
    71  
    72  		registry, err := namespace.ToProto(nsMap)
    73  		require.NoError(t, err)
    74  
    75  		return registry
    76  	}
    77  
    78  	// dynamic namespace registry options
    79  	dynamicOpts := namespace.NewDynamicOptions().
    80  		SetConfigServiceClient(csClient)
    81  	dynamicInit := namespace.NewDynamicInitializer(dynamicOpts)
    82  	testOpts = testOpts.SetNamespaceInitializer(dynamicInit)
    83  
    84  	// initialize value in kv
    85  	_, err = kvStore.Set(dynamicOpts.NamespaceRegistryKey(), protoKey(ns1))
    86  	require.NoError(t, err)
    87  
    88  	// Test setup
    89  	testSetup, err := NewTestSetup(t, testOpts, nil)
    90  	require.NoError(t, err)
    91  	defer testSetup.Close()
    92  
    93  	// Start the server
    94  	log := testSetup.StorageOpts().InstrumentOptions().Logger()
    95  	require.NoError(t, testSetup.StartServer())
    96  
    97  	// Stop the server
    98  	stopped := false
    99  	defer func() {
   100  		stopped = true
   101  		require.NoError(t, testSetup.StopServer())
   102  		log.Info("server is now down")
   103  	}()
   104  
   105  	// NB(bodu): concurrently do work on the leaseManager to ensure no race cond deadlock regressions by
   106  	// calling OpenLease or OpenLatestLease (blocked on DB lock).
   107  	var wg sync.WaitGroup
   108  	leaser := block.NewMockLeaser(ctrl)
   109  	leaseState := block.LeaseState{}
   110  	for i := 0; i < 100; i++ {
   111  		leaseDescriptor := block.LeaseDescriptor{
   112  			Namespace:  ns0.ID(),
   113  			Shard:      uint32(0),
   114  			BlockStart: xtime.Now().Truncate(ns0.Options().RetentionOptions().BlockSize()),
   115  		}
   116  		wg.Add(2)
   117  		go func() {
   118  			wg.Done()
   119  			for !stopped {
   120  				testSetup.BlockLeaseManager().OpenLease(leaser, leaseDescriptor, leaseState)
   121  			}
   122  		}()
   123  		go func() {
   124  			wg.Done()
   125  			for !stopped {
   126  				testSetup.BlockLeaseManager().OpenLatestLease(leaser, leaseDescriptor)
   127  			}
   128  		}()
   129  	}
   130  	wg.Wait()
   131  
   132  	// Write test data
   133  	blockSize := ns0.Options().RetentionOptions().BlockSize()
   134  	now := testSetup.NowFn()()
   135  	seriesMaps := make(map[xtime.UnixNano]generate.SeriesBlock)
   136  	inputData := []generate.BlockConfig{
   137  		{IDs: []string{"foo", "bar"}, NumPoints: 100, Start: now},
   138  		{IDs: []string{"foo", "baz"}, NumPoints: 50, Start: now.Add(blockSize)},
   139  	}
   140  	for _, input := range inputData {
   141  		start := input.Start
   142  		testData := generate.Block(input)
   143  		seriesMaps[start] = testData
   144  	}
   145  	log.Info("test data is now generated")
   146  
   147  	// fail to write to non-existent namespaces
   148  	for _, testData := range seriesMaps {
   149  		require.Error(t, testSetup.WriteBatch(ns0.ID(), testData))
   150  	}
   151  
   152  	// update value in kv
   153  	_, err = kvStore.Set(dynamicOpts.NamespaceRegistryKey(), protoKey(ns0, ns1))
   154  	require.NoError(t, err)
   155  	log.Info("new namespace added to kv")
   156  
   157  	// wait until the new namespace is registered
   158  	nsExists := func() bool {
   159  		_, ok := testSetup.DB().Namespace(ns0.ID())
   160  		return ok
   161  	}
   162  	require.True(t, waitUntil(nsExists, 5*time.Second))
   163  	log.Info("new namespace available in testSetup")
   164  
   165  	// write to new namespace
   166  	for start, testData := range seriesMaps {
   167  		testSetup.SetNowFn(start)
   168  		require.NoError(t, testSetup.WriteBatch(ns0.ID(), testData))
   169  	}
   170  	log.Info("test data is now written")
   171  
   172  	// Advance time and sleep for a long enough time so data blocks are sealed during ticking
   173  	testSetup.SetNowFn(testSetup.NowFn()().Add(2 * blockSize))
   174  	later := testSetup.NowFn()()
   175  	testSetup.SleepFor10xTickMinimumInterval()
   176  
   177  	metadatasByShard := testSetupMetadatas(t, testSetup, ns0.ID(), now, later)
   178  	observedSeriesMaps := testSetupToSeriesMaps(t, testSetup, ns0, metadatasByShard)
   179  	log.Info("reading data from testSetup")
   180  
   181  	// Verify retrieved data matches what we've written
   182  	verifySeriesMapsEqual(t, seriesMaps, observedSeriesMaps)
   183  	log.Info("data is verified")
   184  }