github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/dbnode/integration/dynamic_namespace_delete_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  	"testing"
    27  	"time"
    28  
    29  	"github.com/m3db/m3/src/cluster/integration/etcd"
    30  	"github.com/m3db/m3/src/dbnode/integration/generate"
    31  	"github.com/m3db/m3/src/dbnode/namespace"
    32  	xmetrics "github.com/m3db/m3/src/dbnode/x/metrics"
    33  	"github.com/m3db/m3/src/x/instrument"
    34  	xtime "github.com/m3db/m3/src/x/time"
    35  
    36  	"github.com/golang/protobuf/proto"
    37  	"github.com/stretchr/testify/require"
    38  	"github.com/uber-go/tally"
    39  )
    40  
    41  func TestDynamicNamespaceDelete(t *testing.T) {
    42  	if testing.Short() {
    43  		t.SkipNow() // Just skip if we're doing a short run
    44  	}
    45  
    46  	// test options
    47  	testOpts := NewTestOptions(t).
    48  		SetTickMinimumInterval(time.Second)
    49  	require.True(t, len(testOpts.Namespaces()) >= 2)
    50  	ns0 := testOpts.Namespaces()[0]
    51  	ns1 := testOpts.Namespaces()[1]
    52  
    53  	reporter := xmetrics.NewTestStatsReporter(xmetrics.NewTestStatsReporterOptions())
    54  	scope, closer := tally.NewRootScope(
    55  		tally.ScopeOptions{Reporter: reporter}, time.Millisecond)
    56  	defer closer.Close()
    57  
    58  	// embedded kv
    59  	embeddedKV, err := etcd.New(etcd.NewOptions())
    60  	require.NoError(t, err)
    61  	defer func() {
    62  		require.NoError(t, embeddedKV.Close())
    63  	}()
    64  	require.NoError(t, embeddedKV.Start())
    65  	csClient, err := embeddedKV.ConfigServiceClient()
    66  	require.NoError(t, err)
    67  	kvStore, err := csClient.KV()
    68  	require.NoError(t, err)
    69  
    70  	// namespace maps
    71  	protoKey := func(nses ...namespace.Metadata) proto.Message {
    72  		nsMap, err := namespace.NewMap(nses)
    73  		require.NoError(t, err)
    74  
    75  		registry, err := namespace.ToProto(nsMap)
    76  		require.NoError(t, err)
    77  
    78  		return registry
    79  	}
    80  
    81  	// dynamic namespace registry options
    82  	dynamicOpts := namespace.NewDynamicOptions().
    83  		SetConfigServiceClient(csClient).
    84  		SetInstrumentOptions(instrument.NewOptions().SetMetricsScope(scope))
    85  	dynamicInit := namespace.NewDynamicInitializer(dynamicOpts)
    86  	testOpts = testOpts.SetNamespaceInitializer(dynamicInit)
    87  
    88  	// initialize value in kv
    89  	_, err = kvStore.Set(dynamicOpts.NamespaceRegistryKey(), protoKey(ns1))
    90  	require.NoError(t, err)
    91  
    92  	// Test setup
    93  	testSetup, err := NewTestSetup(t, testOpts, nil)
    94  	require.NoError(t, err)
    95  	defer testSetup.Close()
    96  
    97  	// Start the server
    98  	log := testSetup.StorageOpts().InstrumentOptions().Logger()
    99  	require.NoError(t, testSetup.StartServer())
   100  
   101  	// Stop the server
   102  	defer func() {
   103  		require.NoError(t, testSetup.StopServer())
   104  		log.Info("server is now down")
   105  	}()
   106  
   107  	// Write test data
   108  	blockSize := ns0.Options().RetentionOptions().BlockSize()
   109  	now := testSetup.NowFn()()
   110  	seriesMaps := make(map[xtime.UnixNano]generate.SeriesBlock)
   111  	inputData := []generate.BlockConfig{
   112  		{IDs: []string{"foo", "bar"}, NumPoints: 100, Start: now},
   113  		{IDs: []string{"foo", "baz"}, NumPoints: 50, Start: now.Add(blockSize)},
   114  	}
   115  	for _, input := range inputData {
   116  		start := input.Start
   117  		testData := generate.Block(input)
   118  		seriesMaps[start] = testData
   119  	}
   120  	log.Info("test data is now generated")
   121  
   122  	// fail to write to non-existent namespaces
   123  	for _, testData := range seriesMaps {
   124  		require.Error(t, testSetup.WriteBatch(ns0.ID(), testData))
   125  	}
   126  
   127  	// delete namespace key, ensure update propagates
   128  	numInvalid := numInvalidNamespaceUpdates(reporter)
   129  	_, err = kvStore.Delete(dynamicOpts.NamespaceRegistryKey())
   130  	require.NoError(t, err)
   131  	deletePropagated := func() bool {
   132  		return numInvalidNamespaceUpdates(reporter) > numInvalid
   133  	}
   134  	require.True(t, waitUntil(deletePropagated, 20*time.Second))
   135  	log.Info("deleted namespace key propagated from KV to testSetup")
   136  
   137  	// update value in kv
   138  	_, err = kvStore.Set(dynamicOpts.NamespaceRegistryKey(), protoKey(ns0, ns1))
   139  	require.NoError(t, err)
   140  	log.Info("new namespace added to kv")
   141  
   142  	// wait until the new namespace is registered
   143  	nsExists := func() bool {
   144  		_, ok := testSetup.DB().Namespace(ns0.ID())
   145  		return ok
   146  	}
   147  	require.True(t, waitUntil(nsExists, 5*time.Second))
   148  	log.Info("new namespace propagated from KV to testSetup")
   149  
   150  	// write to new namespace
   151  	for start, testData := range seriesMaps {
   152  		testSetup.SetNowFn(start)
   153  		require.NoError(t, testSetup.WriteBatch(ns0.ID(), testData))
   154  	}
   155  	log.Info("test data is now written")
   156  
   157  	// Advance time and sleep for a long enough time so data blocks are sealed during ticking
   158  	testSetup.SetNowFn(testSetup.NowFn()().Add(2 * blockSize))
   159  	later := testSetup.NowFn()()
   160  	testSetup.SleepFor10xTickMinimumInterval()
   161  
   162  	metadatasByShard := testSetupMetadatas(t, testSetup, ns0.ID(), now, later)
   163  	observedSeriesMaps := testSetupToSeriesMaps(t, testSetup, ns0, metadatasByShard)
   164  	log.Info("reading data from testSetup")
   165  
   166  	// Verify retrieved data matches what we've written
   167  	verifySeriesMapsEqual(t, seriesMaps, observedSeriesMaps)
   168  	log.Info("data is verified")
   169  }
   170  
   171  func numInvalidNamespaceUpdates(reporter xmetrics.TestStatsReporter) int64 {
   172  	return reporter.Counters()["namespace-registry.invalid-update"]
   173  }