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 }