github.com/m3db/m3@v1.5.0/src/dbnode/integration/setup.go (about) 1 // Copyright (c) 2016 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 integration 22 23 import ( 24 "errors" 25 "flag" 26 "fmt" 27 "io/ioutil" 28 "os" 29 "os/exec" 30 "strings" 31 "sync" 32 "testing" 33 "time" 34 35 "github.com/stretchr/testify/require" 36 "github.com/uber-go/tally" 37 "github.com/uber/tchannel-go" 38 "go.uber.org/zap" 39 "go.uber.org/zap/zapcore" 40 41 "github.com/m3db/m3/src/cluster/services" 42 "github.com/m3db/m3/src/cluster/shard" 43 "github.com/m3db/m3/src/dbnode/client" 44 "github.com/m3db/m3/src/dbnode/generated/thrift/rpc" 45 "github.com/m3db/m3/src/dbnode/integration/fake" 46 "github.com/m3db/m3/src/dbnode/integration/generate" 47 "github.com/m3db/m3/src/dbnode/namespace" 48 "github.com/m3db/m3/src/dbnode/persist/fs" 49 "github.com/m3db/m3/src/dbnode/persist/fs/commitlog" 50 "github.com/m3db/m3/src/dbnode/retention" 51 "github.com/m3db/m3/src/dbnode/runtime" 52 "github.com/m3db/m3/src/dbnode/server" 53 "github.com/m3db/m3/src/dbnode/sharding" 54 "github.com/m3db/m3/src/dbnode/storage" 55 "github.com/m3db/m3/src/dbnode/storage/block" 56 "github.com/m3db/m3/src/dbnode/storage/bootstrap" 57 "github.com/m3db/m3/src/dbnode/storage/bootstrap/bootstrapper" 58 bcl "github.com/m3db/m3/src/dbnode/storage/bootstrap/bootstrapper/commitlog" 59 bfs "github.com/m3db/m3/src/dbnode/storage/bootstrap/bootstrapper/fs" 60 "github.com/m3db/m3/src/dbnode/storage/bootstrap/bootstrapper/uninitialized" 61 "github.com/m3db/m3/src/dbnode/storage/cluster" 62 "github.com/m3db/m3/src/dbnode/storage/index" 63 "github.com/m3db/m3/src/dbnode/storage/series" 64 "github.com/m3db/m3/src/dbnode/testdata/prototest" 65 "github.com/m3db/m3/src/dbnode/topology" 66 "github.com/m3db/m3/src/dbnode/ts" 67 "github.com/m3db/m3/src/x/clock" 68 "github.com/m3db/m3/src/x/ident" 69 "github.com/m3db/m3/src/x/instrument" 70 xsync "github.com/m3db/m3/src/x/sync" 71 xtime "github.com/m3db/m3/src/x/time" 72 ) 73 74 var ( 75 id = flag.String("id", "", "Node host ID") 76 httpClusterAddr = flag.String("clusterhttpaddr", "127.0.0.1:9000", "Cluster HTTP server address") 77 tchannelClusterAddr = flag.String("clustertchanneladdr", "127.0.0.1:9001", "Cluster TChannel server address") 78 httpNodeAddr = flag.String("nodehttpaddr", "127.0.0.1:9002", "Node HTTP server address") 79 tchannelNodeAddr = flag.String("nodetchanneladdr", "127.0.0.1:9003", "Node TChannel server address") 80 httpDebugAddr = flag.String("debughttpaddr", "127.0.0.1:9004", "HTTP debug server address") 81 82 errServerStartTimedOut = errors.New("server took too long to start") 83 errServerStopTimedOut = errors.New("server took too long to stop") 84 testNamespaces = []ident.ID{ident.StringID("testNs1"), ident.StringID("testNs2")} 85 86 testSchemaHistory = prototest.NewSchemaHistory() 87 testSchema = prototest.NewMessageDescriptor(testSchemaHistory) 88 testProtoMessages = prototest.NewProtoTestMessages(testSchema) 89 testProtoIter = prototest.NewProtoMessageIterator(testProtoMessages) 90 ) 91 92 // nowSetterFn is the function that sets the current time 93 type nowSetterFn func(t xtime.UnixNano) 94 95 type assertTestDataEqual func(t *testing.T, expected, actual []generate.TestValue) bool 96 97 var _ topology.MapProvider = &testSetup{} 98 99 type testSetup struct { 100 t *testing.T 101 opts TestOptions 102 schemaReg namespace.SchemaRegistry 103 104 logger *zap.Logger 105 scope tally.TestScope 106 107 db cluster.Database 108 storageOpts storage.Options 109 instrumentOpts instrument.Options 110 serverStorageOpts server.StorageOptions 111 fsOpts fs.Options 112 blockLeaseManager block.LeaseManager 113 hostID string 114 origin topology.Host 115 topoInit topology.Initializer 116 shardSet sharding.ShardSet 117 getNowFn xNowFn 118 clockNowFn clock.NowFn 119 setNowFn nowSetterFn 120 tchannelClient *TestTChannelClient 121 m3dbClient client.Client 122 // We need two distinct clients where one has the origin set to the same ID as the 123 // node itself (I.E) the client will behave exactly as if it is the node itself 124 // making requests, and another client with the origin set to an ID different than 125 // the node itself so that we can make requests from the perspective of a "different" 126 // M3DB node for verification purposes in some of the tests. 127 m3dbAdminClient client.AdminClient 128 m3dbVerificationAdminClient client.AdminClient 129 workerPool xsync.WorkerPool 130 131 // compare expected with actual data function 132 assertEqual assertTestDataEqual 133 134 // things that need to be cleaned up 135 channel *tchannel.Channel 136 filePathPrefix string 137 namespaces []namespace.Metadata 138 139 // signals 140 doneCh chan struct{} 141 closedCh chan struct{} 142 } 143 144 type xNowFn func() xtime.UnixNano 145 146 // TestSetup is a test setup. 147 type TestSetup interface { 148 topology.MapProvider 149 150 Opts() TestOptions 151 SetOpts(TestOptions) 152 FilesystemOpts() fs.Options 153 AssertEqual(*testing.T, []generate.TestValue, []generate.TestValue) bool 154 DB() cluster.Database 155 Scope() tally.TestScope 156 M3DBClient() client.Client 157 M3DBVerificationAdminClient() client.AdminClient 158 TChannelClient() *TestTChannelClient 159 Namespaces() []namespace.Metadata 160 TopologyInitializer() topology.Initializer 161 SetTopologyInitializer(topology.Initializer) 162 Fetch(req *rpc.FetchRequest) ([]generate.TestValue, error) 163 FilePathPrefix() string 164 StorageOpts() storage.Options 165 SetStorageOpts(storage.Options) 166 SetServerStorageOpts(server.StorageOptions) 167 Origin() topology.Host 168 ServerIsBootstrapped() bool 169 StopServer() error 170 StopServerAndVerifyOpenFilesAreClosed() error 171 StartServer() error 172 StartServerDontWaitBootstrap() error 173 NowFn() xNowFn 174 ClockNowFn() clock.NowFn 175 SetNowFn(xtime.UnixNano) 176 Close() 177 WriteBatch(ident.ID, generate.SeriesBlock) error 178 ShouldBeEqual() bool 179 // *NOTE*: This method is deprecated and should not be used in future tests. 180 // Also, we should migrate existing tests when we touch them away from using this. 181 SleepFor10xTickMinimumInterval() 182 BlockLeaseManager() block.LeaseManager 183 ShardSet() sharding.ShardSet 184 SetShardSet(sharding.ShardSet) 185 GeneratorOptions(retention.Options) generate.Options 186 MaybeResetClients() error 187 SchemaRegistry() namespace.SchemaRegistry 188 NamespaceMetadataOrFail(ident.ID) namespace.Metadata 189 MustSetTickMinimumInterval(time.Duration) 190 WaitUntilServerIsBootstrapped() error 191 WaitUntilServerIsUp() error 192 WaitUntilServerIsDown() error 193 Truncate(*rpc.TruncateRequest) (int64, error) 194 InitializeBootstrappers(opts InitializeBootstrappersOptions) error 195 } 196 197 // StorageOption is a reference to storage options function. 198 type StorageOption func(storage.Options) storage.Options 199 200 // NewTestSetup returns a new test setup for non-dockerized integration tests. 201 func NewTestSetup( 202 t *testing.T, 203 opts TestOptions, 204 fsOpts fs.Options, 205 storageOptFns ...StorageOption, 206 ) (TestSetup, error) { 207 if opts == nil { 208 opts = NewTestOptions(t) 209 } 210 211 nsInit := opts.NamespaceInitializer() 212 if nsInit == nil { 213 nsInit = namespace.NewStaticInitializer(opts.Namespaces()) 214 } 215 216 zapConfig := zap.NewDevelopmentConfig() 217 zapConfig.DisableCaller = true 218 zapConfig.Level = zap.NewAtomicLevelAt(zapcore.InfoLevel) 219 if level := os.Getenv("LOG_LEVEL"); level != "" { 220 var parsedLevel zap.AtomicLevel 221 if err := parsedLevel.UnmarshalText([]byte(level)); err != nil { 222 return nil, fmt.Errorf("unable to parse log level: %v", err) 223 } 224 zapConfig.Level = parsedLevel 225 } 226 logger, err := zapConfig.Build() 227 if err != nil { 228 return nil, err 229 } 230 231 // Schema registry is shared between database and admin client. 232 schemaReg := namespace.NewSchemaRegistry(opts.ProtoEncoding(), nil) 233 234 blockLeaseManager := block.NewLeaseManager(nil) 235 storageOpts := storage.NewOptions(). 236 SetNamespaceInitializer(nsInit). 237 SetSchemaRegistry(schemaReg). 238 SetBlockLeaseManager(blockLeaseManager) 239 240 if opts.ProtoEncoding() { 241 blockOpts := storageOpts.DatabaseBlockOptions(). 242 SetEncoderPool(prototest.ProtoPools.EncoderPool). 243 SetReaderIteratorPool(prototest.ProtoPools.ReaderIterPool). 244 SetMultiReaderIteratorPool(prototest.ProtoPools.MultiReaderIterPool) 245 storageOpts = storageOpts. 246 SetDatabaseBlockOptions(blockOpts). 247 SetEncoderPool(prototest.ProtoPools.EncoderPool). 248 SetReaderIteratorPool(prototest.ProtoPools.ReaderIterPool). 249 SetMultiReaderIteratorPool(prototest.ProtoPools.MultiReaderIterPool) 250 } 251 252 if strings.ToLower(os.Getenv("TEST_DEBUG_LOG")) == "true" { 253 zapConfig.Level = zap.NewAtomicLevelAt(zapcore.DebugLevel) 254 logger, err = zapConfig.Build() 255 if err != nil { 256 return nil, err 257 } 258 storageOpts = storageOpts.SetInstrumentOptions( 259 storageOpts.InstrumentOptions().SetLogger(logger)) 260 } 261 262 scope := tally.NewTestScope("", nil) 263 storageOpts = storageOpts.SetInstrumentOptions( 264 storageOpts.InstrumentOptions().SetMetricsScope(scope)) 265 266 // Use specified series cache policy from environment if set. 267 seriesCachePolicy := strings.ToLower(os.Getenv("TEST_SERIES_CACHE_POLICY")) 268 if seriesCachePolicy != "" { 269 value, err := series.ParseCachePolicy(seriesCachePolicy) 270 if err != nil { 271 return nil, err 272 } 273 storageOpts = storageOpts.SetSeriesCachePolicy(value) 274 } 275 276 fields := []zapcore.Field{ 277 zap.Stringer("cache-policy", storageOpts.SeriesCachePolicy()), 278 } 279 logger = logger.With(fields...) 280 instrumentOpts := storageOpts.InstrumentOptions().SetLogger(logger) 281 storageOpts = storageOpts.SetInstrumentOptions(instrumentOpts) 282 283 indexMode := index.InsertSync 284 if opts.WriteNewSeriesAsync() { 285 indexMode = index.InsertAsync 286 } 287 288 plCache, err := index.NewPostingsListCache(10, index.PostingsListCacheOptions{ 289 InstrumentOptions: instrumentOpts, 290 }) 291 if err != nil { 292 return nil, fmt.Errorf("unable to create postings list cache: %v", err) 293 } 294 // Ok to run immediately since it just closes the background reporting loop. Only ok because 295 // this is a test setup, in production we would want the metrics. 296 plCache.Start()() 297 298 indexOpts := storageOpts.IndexOptions(). 299 SetInsertMode(indexMode). 300 SetPostingsListCache(plCache) 301 storageOpts = storageOpts.SetIndexOptions(indexOpts) 302 303 runtimeOptsMgr := storageOpts.RuntimeOptionsManager() 304 runtimeOpts := runtimeOptsMgr.Get(). 305 SetTickMinimumInterval(opts.TickMinimumInterval()). 306 SetTickCancellationCheckInterval(opts.TickCancellationCheckInterval()). 307 SetMaxWiredBlocks(opts.MaxWiredBlocks()). 308 SetWriteNewSeriesAsync(opts.WriteNewSeriesAsync()) 309 if err := runtimeOptsMgr.Update(runtimeOpts); err != nil { 310 return nil, err 311 } 312 313 // Set up shard set. 314 shardSet, err := newTestShardSet(opts.NumShards(), opts.ShardSetOptions()) 315 if err != nil { 316 return nil, err 317 } 318 319 id := *id 320 if id == "" { 321 id = opts.ID() 322 } 323 324 tchannelNodeAddr := *tchannelNodeAddr 325 if addr := opts.TChannelNodeAddr(); addr != "" { 326 tchannelNodeAddr = addr 327 } 328 329 topoInit := opts.ClusterDatabaseTopologyInitializer() 330 if topoInit == nil { 331 topoInit, err = newTopologyInitializerForShardSet(id, tchannelNodeAddr, shardSet) 332 if err != nil { 333 return nil, err 334 } 335 } 336 337 adminClient, verificationAdminClient, err := newClients(topoInit, opts, 338 schemaReg, id, tchannelNodeAddr, instrumentOpts) 339 if err != nil { 340 return nil, err 341 } 342 343 // Set up tchannel client 344 tchanClient, err := NewTChannelClient("integration-test", tchannelNodeAddr) 345 if err != nil { 346 return nil, err 347 } 348 349 // Set up worker pool 350 workerPool := xsync.NewWorkerPool(opts.WorkerPoolSize()) 351 workerPool.Init() 352 353 // BlockSizes are specified per namespace, make best effort at finding 354 // a value to align `now` for all of them. 355 truncateSize, guess := guessBestTruncateBlockSize(opts.Namespaces()) 356 if guess { 357 logger.Warn("unable to find a single blockSize from known retention periods", 358 zap.String("guessing", truncateSize.String())) 359 } 360 361 // Set up getter and setter for now 362 var lock sync.RWMutex 363 now := xtime.Now().Truncate(truncateSize) 364 getNowFn := func() xtime.UnixNano { 365 lock.RLock() 366 t := now 367 lock.RUnlock() 368 return t 369 } 370 clockNowFn := func() time.Time { 371 return getNowFn().ToTime() 372 } 373 setNowFn := func(t xtime.UnixNano) { 374 lock.Lock() 375 now = t 376 lock.Unlock() 377 } 378 if overrideTimeNow := opts.NowFn(); overrideTimeNow != nil { 379 // Allow overriding the frozen time 380 storageOpts = storageOpts.SetClockOptions( 381 storageOpts.ClockOptions().SetNowFn(overrideTimeNow)) 382 } else { 383 storageOpts = storageOpts.SetClockOptions( 384 storageOpts.ClockOptions().SetNowFn(clockNowFn)) 385 } 386 387 // Set up file path prefix 388 filePathPrefix := opts.FilePathPrefix() 389 if filePathPrefix == "" { 390 var err error 391 filePathPrefix, err = ioutil.TempDir("", "integration-test") 392 if err != nil { 393 return nil, err 394 } 395 } 396 397 if fsOpts == nil { 398 fsOpts = fs.NewOptions(). 399 SetFilePathPrefix(filePathPrefix). 400 SetClockOptions(storageOpts.ClockOptions()) 401 } 402 403 storageOpts = storageOpts.SetCommitLogOptions( 404 storageOpts.CommitLogOptions(). 405 SetFilesystemOptions(fsOpts)) 406 407 // Set up persistence manager 408 pm, err := fs.NewPersistManager(fsOpts) 409 if err != nil { 410 return nil, err 411 } 412 storageOpts = storageOpts.SetPersistManager(pm) 413 414 // Set up index claims manager 415 icm, err := fs.NewIndexClaimsManager(fsOpts) 416 if err != nil { 417 return nil, err 418 } 419 storageOpts = storageOpts.SetIndexClaimsManager(icm) 420 421 // Set up repair options 422 storageOpts = storageOpts. 423 SetRepairOptions(storageOpts.RepairOptions(). 424 SetAdminClients([]client.AdminClient{adminClient})) 425 426 // Set up block retriever manager 427 if mgr := opts.DatabaseBlockRetrieverManager(); mgr != nil { 428 storageOpts = storageOpts.SetDatabaseBlockRetrieverManager(mgr) 429 } else { 430 switch storageOpts.SeriesCachePolicy() { 431 case series.CacheAll: 432 // Do not need a block retriever for CacheAll policy 433 default: 434 blockRetrieverMgr := block.NewDatabaseBlockRetrieverManager( 435 func(md namespace.Metadata, shardSet sharding.ShardSet) (block.DatabaseBlockRetriever, error) { 436 retrieverOpts := fs.NewBlockRetrieverOptions(). 437 SetBlockLeaseManager(blockLeaseManager) 438 retriever, err := fs.NewBlockRetriever(retrieverOpts, fsOpts) 439 if err != nil { 440 return nil, err 441 } 442 443 if err := retriever.Open(md, shardSet); err != nil { 444 return nil, err 445 } 446 return retriever, nil 447 }) 448 storageOpts = storageOpts. 449 SetDatabaseBlockRetrieverManager(blockRetrieverMgr) 450 } 451 } 452 453 // Set up wired list if required 454 if storageOpts.SeriesCachePolicy() == series.CacheLRU { 455 wiredList := block.NewWiredList(block.WiredListOptions{ 456 RuntimeOptionsManager: runtimeOptsMgr, 457 InstrumentOptions: storageOpts.InstrumentOptions(), 458 ClockOptions: storageOpts.ClockOptions(), 459 // Use a small event channel size to stress-test the implementation 460 EventsChannelSize: 1, 461 }) 462 blockOpts := storageOpts.DatabaseBlockOptions().SetWiredList(wiredList) 463 blockPool := block.NewDatabaseBlockPool(nil) 464 // Have to manually set the blockpool because the default one uses a constructor 465 // function that doesn't have the updated blockOpts. 466 blockPool.Init(func() block.DatabaseBlock { 467 return block.NewDatabaseBlock(0, 0, ts.Segment{}, blockOpts, namespace.Context{}) 468 }) 469 blockOpts = blockOpts.SetDatabaseBlockPool(blockPool) 470 storageOpts = storageOpts.SetDatabaseBlockOptions(blockOpts) 471 } 472 473 storageOpts = storageOpts.SetInstrumentOptions( 474 storageOpts.InstrumentOptions().SetReportInterval(opts.ReportInterval())) 475 476 // Set debugging options if environment vars set 477 if debugFilePrefix := os.Getenv("TEST_DEBUG_FILE_PREFIX"); debugFilePrefix != "" { 478 opts = opts.SetVerifySeriesDebugFilePathPrefix(debugFilePrefix) 479 } 480 481 for _, fn := range storageOptFns { 482 if fn != nil { 483 storageOpts = fn(storageOpts) 484 } 485 } 486 if storageOpts != nil && storageOpts.AdminClient() == nil { 487 storageOpts = storageOpts.SetAdminClient(adminClient) 488 } 489 490 return &testSetup{ 491 t: t, 492 opts: opts, 493 schemaReg: schemaReg, 494 logger: logger, 495 scope: scope, 496 storageOpts: storageOpts, 497 blockLeaseManager: blockLeaseManager, 498 instrumentOpts: instrumentOpts, 499 fsOpts: fsOpts, 500 hostID: id, 501 origin: newOrigin(id, tchannelNodeAddr), 502 topoInit: topoInit, 503 shardSet: shardSet, 504 getNowFn: getNowFn, 505 clockNowFn: clockNowFn, 506 setNowFn: setNowFn, 507 tchannelClient: tchanClient, 508 m3dbClient: adminClient.(client.Client), 509 m3dbAdminClient: adminClient, 510 m3dbVerificationAdminClient: verificationAdminClient, 511 workerPool: workerPool, 512 filePathPrefix: filePathPrefix, 513 namespaces: opts.Namespaces(), 514 doneCh: make(chan struct{}), 515 closedCh: make(chan struct{}), 516 assertEqual: opts.AssertTestDataEqual(), 517 }, nil 518 } 519 520 // guestBestTruncateBlockSize guesses for the best block size to truncate testSetup's nowFn 521 func guessBestTruncateBlockSize(mds []namespace.Metadata) (time.Duration, bool) { 522 // gcd of a pair of numbers 523 gcd := func(a, b int64) int64 { 524 for b > 0 { 525 a, b = b, a%b 526 } 527 return a 528 } 529 lcm := func(a, b int64) int64 { 530 return a * b / gcd(a, b) 531 } 532 533 // default guess 534 if len(mds) == 0 { 535 return time.Hour, true 536 } 537 538 // get all known blocksizes 539 blockSizes := make(map[int64]struct{}) 540 for _, md := range mds { 541 bs := md.Options().RetentionOptions().BlockSize().Nanoseconds() / int64(time.Millisecond) 542 blockSizes[bs] = struct{}{} 543 } 544 545 first := true 546 var l int64 547 for i := range blockSizes { 548 if first { 549 l = i 550 first = false 551 } else { 552 l = lcm(l, i) 553 } 554 } 555 556 guess := time.Duration(l) * time.Millisecond 557 // if there's only a single value, we are not guessing 558 if len(blockSizes) == 1 { 559 return guess, false 560 } 561 562 // otherwise, we are guessing 563 return guess, true 564 } 565 566 func (ts *testSetup) ShouldBeEqual() bool { 567 return ts.assertEqual == nil 568 } 569 570 func (ts *testSetup) AssertEqual(t *testing.T, a, b []generate.TestValue) bool { 571 return ts.assertEqual(t, a, b) 572 } 573 574 func (ts *testSetup) DB() cluster.Database { 575 return ts.db 576 } 577 578 func (ts *testSetup) Scope() tally.TestScope { 579 return ts.scope 580 } 581 582 func (ts *testSetup) M3DBClient() client.Client { 583 return ts.m3dbClient 584 } 585 586 func (ts *testSetup) M3DBVerificationAdminClient() client.AdminClient { 587 return ts.m3dbVerificationAdminClient 588 } 589 590 func (ts *testSetup) Namespaces() []namespace.Metadata { 591 return ts.namespaces 592 } 593 594 func (ts *testSetup) NowFn() xNowFn { 595 return ts.getNowFn 596 } 597 598 func (ts *testSetup) ClockNowFn() clock.NowFn { 599 return ts.clockNowFn 600 } 601 602 func (ts *testSetup) SetNowFn(t xtime.UnixNano) { 603 ts.setNowFn(t) 604 } 605 606 func (ts *testSetup) FilesystemOpts() fs.Options { 607 return ts.fsOpts 608 } 609 610 func (ts *testSetup) Opts() TestOptions { 611 return ts.opts 612 } 613 614 func (ts *testSetup) SetOpts(opts TestOptions) { 615 ts.opts = opts 616 } 617 618 func (ts *testSetup) Origin() topology.Host { 619 return ts.origin 620 } 621 622 func (ts *testSetup) FilePathPrefix() string { 623 return ts.filePathPrefix 624 } 625 626 func (ts *testSetup) StorageOpts() storage.Options { 627 return ts.storageOpts 628 } 629 630 func (ts *testSetup) SetStorageOpts(opts storage.Options) { 631 ts.storageOpts = opts 632 } 633 634 func (ts *testSetup) SetServerStorageOpts(opts server.StorageOptions) { 635 ts.serverStorageOpts = opts 636 } 637 638 func (ts *testSetup) TopologyInitializer() topology.Initializer { 639 return ts.topoInit 640 } 641 642 func (ts *testSetup) SetTopologyInitializer(init topology.Initializer) { 643 ts.topoInit = init 644 } 645 646 func (ts *testSetup) BlockLeaseManager() block.LeaseManager { 647 return ts.blockLeaseManager 648 } 649 650 func (ts *testSetup) ShardSet() sharding.ShardSet { 651 return ts.shardSet 652 } 653 654 func (ts *testSetup) SetShardSet(shardSet sharding.ShardSet) { 655 ts.shardSet = shardSet 656 } 657 658 func (ts *testSetup) NamespaceMetadataOrFail(id ident.ID) namespace.Metadata { 659 for _, md := range ts.namespaces { 660 if md.ID().Equal(id) { 661 return md 662 } 663 } 664 require.FailNow(ts.t, "unable to find namespace", id.String()) 665 return nil 666 } 667 668 func (ts *testSetup) GeneratorOptions(ropts retention.Options) generate.Options { 669 var ( 670 storageOpts = ts.storageOpts 671 fsOpts = storageOpts.CommitLogOptions().FilesystemOptions() 672 opts = generate.NewOptions() 673 co = opts.ClockOptions().SetNowFn(ts.clockNowFn) 674 ) 675 676 return opts. 677 SetClockOptions(co). 678 SetRetentionPeriod(ropts.RetentionPeriod()). 679 SetBlockSize(ropts.BlockSize()). 680 SetFilePathPrefix(fsOpts.FilePathPrefix()). 681 SetNewFileMode(fsOpts.NewFileMode()). 682 SetNewDirectoryMode(fsOpts.NewDirectoryMode()). 683 SetWriterBufferSize(fsOpts.WriterBufferSize()). 684 SetEncoderPool(storageOpts.EncoderPool()) 685 } 686 687 func (ts *testSetup) ServerIsBootstrapped() bool { 688 resp, err := ts.health() 689 return err == nil && resp.Bootstrapped 690 } 691 692 func (ts *testSetup) ServerIsUp() bool { 693 _, err := ts.health() 694 return err == nil 695 } 696 697 func (ts *testSetup) ServerIsDown() bool { 698 return !ts.ServerIsUp() 699 } 700 701 func (ts *testSetup) WaitUntilServerIsBootstrapped() error { 702 if waitUntil(ts.ServerIsBootstrapped, ts.opts.ServerStateChangeTimeout()) { 703 return nil 704 } 705 return errServerStartTimedOut 706 } 707 708 func (ts *testSetup) WaitUntilServerIsUp() error { 709 if waitUntil(ts.ServerIsUp, ts.opts.ServerStateChangeTimeout()) { 710 return nil 711 } 712 return errServerStopTimedOut 713 } 714 715 func (ts *testSetup) WaitUntilServerIsDown() error { 716 if waitUntil(ts.ServerIsDown, ts.opts.ServerStateChangeTimeout()) { 717 return nil 718 } 719 return errServerStopTimedOut 720 } 721 722 func (ts *testSetup) StartServerDontWaitBootstrap() error { 723 return ts.startServerBase(false) 724 } 725 726 func (ts *testSetup) StartServer() error { 727 return ts.startServerBase(true) 728 } 729 730 func (ts *testSetup) startServerBase(waitForBootstrap bool) error { 731 ts.logger.Info("starting server") 732 733 var ( 734 resultCh = make(chan error, 1) 735 err error 736 ) 737 738 topo, err := ts.topoInit.Init() 739 if err != nil { 740 return fmt.Errorf("error initializing topology: %v", err) 741 } 742 743 topoWatch, err := topo.Watch() 744 if err != nil { 745 return fmt.Errorf("error watching topology: %v", err) 746 } 747 748 ts.db, err = cluster.NewDatabase(ts.hostID, topo, topoWatch, ts.storageOpts) 749 if err != nil { 750 return err 751 } 752 753 leaseVerifier := storage.NewLeaseVerifier(ts.db) 754 if err := ts.blockLeaseManager.SetLeaseVerifier(leaseVerifier); err != nil { 755 return err 756 } 757 758 // Check if clients were closed by StopServer and need to be re-created. 759 ts.MaybeResetClients() 760 761 go func() { 762 if err := openAndServe( 763 ts.httpClusterAddr(), ts.tchannelClusterAddr(), 764 ts.httpNodeAddr(), ts.tchannelNodeAddr(), ts.httpDebugAddr(), 765 ts.db, ts.m3dbClient, ts.storageOpts, ts.serverStorageOpts, ts.doneCh, 766 ); err != nil { 767 select { 768 case resultCh <- err: 769 default: 770 } 771 } 772 773 ts.closedCh <- struct{}{} 774 }() 775 776 waitFn := ts.WaitUntilServerIsUp 777 if waitForBootstrap { 778 waitFn = ts.WaitUntilServerIsBootstrapped 779 } 780 go func() { 781 select { 782 case resultCh <- waitFn(): 783 default: 784 } 785 }() 786 787 err = <-resultCh 788 if err == nil { 789 ts.logger.Info("started server") 790 } else { 791 ts.logger.Error("start server error", zap.Error(err)) 792 } 793 return err 794 } 795 796 func (ts *testSetup) StopServer() error { 797 ts.doneCh <- struct{}{} 798 799 // NB(bodu): Need to reset the global counter of index claims managers after 800 // we've stopped the test server. This covers the restart server case. 801 fs.ResetIndexClaimsManagersUnsafe() 802 803 if ts.m3dbClient.DefaultSessionActive() { 804 session, err := ts.m3dbClient.DefaultSession() 805 if err != nil { 806 return err 807 } 808 ts.m3dbClient = nil 809 ts.m3dbAdminClient = nil 810 ts.m3dbVerificationAdminClient = nil 811 defer session.Close() 812 } 813 814 if err := ts.WaitUntilServerIsDown(); err != nil { 815 return err 816 } 817 818 // Wait for graceful server close 819 <-ts.closedCh 820 return nil 821 } 822 823 func (ts *testSetup) StopServerAndVerifyOpenFilesAreClosed() error { 824 if err := ts.DB().Close(); err != nil { 825 return err 826 } 827 828 openDataFiles := openFiles(ts.filePathPrefix + "/data/") 829 require.Empty(ts.t, openDataFiles) 830 831 return ts.StopServer() 832 } 833 834 // counts open/locked files inside parent dir. 835 func openFiles(parentDir string) []string { 836 cmd := exec.Command("lsof", "+D", parentDir) // nolint:gosec 837 838 out, _ := cmd.Output() 839 if len(out) == 0 { 840 return nil 841 } 842 843 return strings.Split(string(out), "\n") 844 } 845 846 func (ts *testSetup) TChannelClient() *TestTChannelClient { 847 return ts.tchannelClient 848 } 849 850 func (ts *testSetup) WriteBatch(namespace ident.ID, seriesList generate.SeriesBlock) error { 851 if ts.opts.UseTChannelClientForWriting() { 852 return ts.tchannelClient.TChannelClientWriteBatch( 853 ts.opts.WriteRequestTimeout(), namespace, seriesList) 854 } 855 return m3dbClientWriteBatch(ts.m3dbClient, ts.workerPool, namespace, seriesList) 856 } 857 858 func (ts *testSetup) Fetch(req *rpc.FetchRequest) ([]generate.TestValue, error) { 859 if ts.opts.UseTChannelClientForReading() { 860 fetched, err := ts.tchannelClient.TChannelClientFetch(ts.opts.ReadRequestTimeout(), req) 861 if err != nil { 862 return nil, err 863 } 864 dp := toDatapoints(fetched) 865 return dp, nil 866 } 867 return m3dbClientFetch(ts.m3dbClient, req) 868 } 869 870 func (ts *testSetup) Truncate(req *rpc.TruncateRequest) (int64, error) { 871 if ts.opts.UseTChannelClientForTruncation() { 872 return ts.tchannelClient.TChannelClientTruncate(ts.opts.TruncateRequestTimeout(), req) 873 } 874 return m3dbClientTruncate(ts.m3dbClient, req) 875 } 876 877 func (ts *testSetup) health() (*rpc.NodeHealthResult_, error) { 878 return ts.tchannelClient.TChannelClientHealth(5 * time.Second) 879 } 880 881 func (ts *testSetup) Close() { 882 if ts.channel != nil { 883 ts.channel.Close() 884 } 885 if ts.filePathPrefix != "" { 886 os.RemoveAll(ts.filePathPrefix) 887 } 888 889 // This could get called more than once in the multi node integration test case 890 // but this is fine since the reset always sets the counter to 0. 891 fs.ResetIndexClaimsManagersUnsafe() 892 } 893 894 func (ts *testSetup) MustSetTickMinimumInterval(tickMinInterval time.Duration) { 895 runtimeMgr := ts.storageOpts.RuntimeOptionsManager() 896 existingOptions := runtimeMgr.Get() 897 newOptions := existingOptions.SetTickMinimumInterval(tickMinInterval) 898 err := runtimeMgr.Update(newOptions) 899 if err != nil { 900 panic(fmt.Sprintf("err setting tick minimum interval: %v", err)) 901 } 902 } 903 904 // convenience wrapper used to ensure a tick occurs 905 func (ts *testSetup) SleepFor10xTickMinimumInterval() { 906 // Check the runtime options manager instead of relying on ts.opts 907 // because the tick interval can change at runtime. 908 runtimeMgr := ts.storageOpts.RuntimeOptionsManager() 909 opts := runtimeMgr.Get() 910 time.Sleep(opts.TickMinimumInterval() * 10) 911 } 912 913 func (ts *testSetup) httpClusterAddr() string { 914 if addr := ts.opts.HTTPClusterAddr(); addr != "" { 915 return addr 916 } 917 return *httpClusterAddr 918 } 919 920 func (ts *testSetup) httpNodeAddr() string { 921 if addr := ts.opts.HTTPNodeAddr(); addr != "" { 922 return addr 923 } 924 return *httpNodeAddr 925 } 926 927 func (ts *testSetup) tchannelClusterAddr() string { 928 if addr := ts.opts.TChannelClusterAddr(); addr != "" { 929 return addr 930 } 931 return *tchannelClusterAddr 932 } 933 934 func (ts *testSetup) tchannelNodeAddr() string { 935 if addr := ts.opts.TChannelNodeAddr(); addr != "" { 936 return addr 937 } 938 return *tchannelNodeAddr 939 } 940 941 func (ts *testSetup) httpDebugAddr() string { 942 if addr := ts.opts.HTTPDebugAddr(); addr != "" { 943 return addr 944 } 945 return *httpDebugAddr 946 } 947 948 func (ts *testSetup) MaybeResetClients() error { 949 if ts.m3dbClient == nil { 950 // Recreate the clients as their session was destroyed by StopServer() 951 adminClient, verificationAdminClient, err := newClients(ts.topoInit, 952 ts.opts, ts.schemaReg, ts.hostID, ts.tchannelNodeAddr(), 953 ts.instrumentOpts) 954 if err != nil { 955 return err 956 } 957 ts.m3dbClient = adminClient.(client.Client) 958 ts.m3dbAdminClient = adminClient 959 ts.m3dbVerificationAdminClient = verificationAdminClient 960 } 961 962 return nil 963 } 964 965 func (ts *testSetup) SchemaRegistry() namespace.SchemaRegistry { 966 return ts.schemaReg 967 } 968 969 // InitializeBootstrappersOptions supplies options for bootstrapper initialization. 970 type InitializeBootstrappersOptions struct { 971 CommitLogOptions commitlog.Options 972 WithCommitLog bool 973 WithFileSystem bool 974 } 975 976 func (o InitializeBootstrappersOptions) validate() error { 977 if o.WithCommitLog && o.CommitLogOptions == nil { 978 return errors.New("commit log options required when initializing a commit log bootstrapper") 979 } 980 return nil 981 } 982 983 func (ts *testSetup) InitializeBootstrappers(opts InitializeBootstrappersOptions) error { 984 var err error 985 if err := opts.validate(); err != nil { 986 return err 987 } 988 989 bs := bootstrapper.NewNoOpAllBootstrapperProvider() 990 storageOpts := ts.StorageOpts() 991 bsOpts := newDefaulTestResultOptions(storageOpts) 992 fsOpts := storageOpts.CommitLogOptions().FilesystemOptions() 993 if opts.WithCommitLog { 994 bclOpts := bcl.NewOptions(). 995 SetResultOptions(bsOpts). 996 SetCommitLogOptions(opts.CommitLogOptions). 997 SetRuntimeOptionsManager(runtime.NewOptionsManager()) 998 bs, err = bcl.NewCommitLogBootstrapperProvider( 999 bclOpts, mustInspectFilesystem(fsOpts), bs) 1000 if err != nil { 1001 return err 1002 } 1003 } 1004 1005 if opts.WithFileSystem { 1006 persistMgr, err := fs.NewPersistManager(fsOpts) 1007 if err != nil { 1008 return err 1009 } 1010 storageIdxOpts := storageOpts.IndexOptions() 1011 compactor, err := newCompactorWithErr(storageIdxOpts) 1012 if err != nil { 1013 return err 1014 } 1015 bfsOpts := bfs.NewOptions(). 1016 SetResultOptions(bsOpts). 1017 SetFilesystemOptions(fsOpts). 1018 SetIndexOptions(storageIdxOpts). 1019 SetPersistManager(persistMgr). 1020 SetIndexClaimsManager(storageOpts.IndexClaimsManager()). 1021 SetCompactor(compactor). 1022 SetInstrumentOptions(storageOpts.InstrumentOptions()) 1023 bs, err = bfs.NewFileSystemBootstrapperProvider(bfsOpts, bs) 1024 if err != nil { 1025 return err 1026 } 1027 } 1028 1029 processOpts := bootstrap.NewProcessOptions(). 1030 SetTopologyMapProvider(ts). 1031 SetOrigin(ts.Origin()) 1032 process, err := bootstrap.NewProcessProvider(bs, processOpts, bsOpts, fsOpts) 1033 if err != nil { 1034 return err 1035 } 1036 ts.SetStorageOpts(storageOpts.SetBootstrapProcessProvider(process)) 1037 1038 return nil 1039 } 1040 1041 // Implements topology.MapProvider, and makes sure that the topology 1042 // map provided always comes from the most recent database in the testSetup 1043 // since they get\ recreated everytime StartServer/StopServer is called and 1044 // are not available (nil value) after creation but before the first call 1045 // to StartServer. 1046 func (ts *testSetup) TopologyMap() (topology.Map, error) { 1047 return ts.db.TopologyMap() 1048 } 1049 1050 func newOrigin(id string, tchannelNodeAddr string) topology.Host { 1051 return topology.NewHost(id, tchannelNodeAddr) 1052 } 1053 1054 func newClients( 1055 topoInit topology.Initializer, 1056 opts TestOptions, 1057 schemaReg namespace.SchemaRegistry, 1058 id, tchannelNodeAddr string, 1059 instrumentOpts instrument.Options, 1060 ) (client.AdminClient, client.AdminClient, error) { 1061 var ( 1062 clientOpts = defaultClientOptions(topoInit).SetClusterConnectTimeout( 1063 opts.ClusterConnectionTimeout()). 1064 SetFetchRequestTimeout(opts.FetchRequestTimeout()). 1065 SetWriteConsistencyLevel(opts.WriteConsistencyLevel()). 1066 SetTopologyInitializer(topoInit). 1067 SetUseV2BatchAPIs(true). 1068 SetInstrumentOptions(instrumentOpts) 1069 1070 origin = newOrigin(id, tchannelNodeAddr) 1071 verificationOrigin = newOrigin(id+"-verification", tchannelNodeAddr) 1072 1073 adminOpts = clientOpts.(client.AdminOptions).SetOrigin(origin).SetSchemaRegistry(schemaReg) 1074 1075 verificationAdminOpts = adminOpts.SetOrigin(verificationOrigin).SetSchemaRegistry(schemaReg) 1076 ) 1077 1078 if opts.ProtoEncoding() { 1079 adminOpts = adminOpts.SetEncodingProto(prototest.ProtoPools.EncodingOpt).(client.AdminOptions) 1080 verificationAdminOpts = verificationAdminOpts.SetEncodingProto(prototest.ProtoPools.EncodingOpt).(client.AdminOptions) 1081 } 1082 1083 for _, opt := range opts.CustomClientAdminOptions() { 1084 adminOpts = opt(adminOpts) 1085 verificationAdminOpts = opt(verificationAdminOpts) 1086 } 1087 1088 // Set up m3db client 1089 adminClient, err := m3dbAdminClient(adminOpts) 1090 if err != nil { 1091 return nil, nil, err 1092 } 1093 1094 // Set up m3db verification client 1095 verificationAdminClient, err := m3dbAdminClient(verificationAdminOpts) 1096 if err != nil { 1097 return nil, nil, err 1098 } 1099 1100 return adminClient, verificationAdminClient, nil 1101 } 1102 1103 type testSetups []TestSetup 1104 1105 func (ts testSetups) parallel(fn func(s TestSetup)) { 1106 var wg sync.WaitGroup 1107 for _, setup := range ts { 1108 s := setup 1109 wg.Add(1) 1110 go func() { 1111 fn(s) 1112 wg.Done() 1113 }() 1114 } 1115 wg.Wait() 1116 } 1117 1118 // node generates service instances with reasonable defaults 1119 func node(t *testing.T, n int, shards shard.Shards) services.ServiceInstance { 1120 require.True(t, n < 250) // keep ports sensible 1121 return services.NewServiceInstance(). 1122 SetInstanceID(fmt.Sprintf("testhost%v", n)). 1123 SetEndpoint(fmt.Sprintf("127.0.0.1:%v", multiAddrPortStart+multiAddrPortEach*n)). 1124 SetShards(shards) 1125 } 1126 1127 // newNodes creates a set of testSetups with reasonable defaults 1128 func newNodes( 1129 t *testing.T, 1130 numShards int, 1131 instances []services.ServiceInstance, 1132 nspaces []namespace.Metadata, 1133 asyncInserts bool, 1134 ) (testSetups, topology.Initializer, closeFn) { 1135 var ( 1136 log = zap.L() 1137 opts = NewTestOptions(t). 1138 SetNamespaces(nspaces). 1139 SetTickMinimumInterval(3 * time.Second). 1140 SetWriteNewSeriesAsync(asyncInserts). 1141 SetNumShards(numShards) 1142 // NB(bl): We set replication to 3 to mimic production. This can be made 1143 // into a variable if needed. 1144 svc = fake.NewM3ClusterService(). 1145 SetInstances(instances). 1146 SetReplication(services.NewServiceReplication().SetReplicas(3)). 1147 SetSharding(services.NewServiceSharding().SetNumShards(numShards)) 1148 1149 svcs = fake.NewM3ClusterServices() 1150 ) 1151 svcs.RegisterService("m3db", svc) 1152 1153 topoOpts := topology.NewDynamicOptions(). 1154 SetConfigServiceClient(fake.NewM3ClusterClient(svcs, nil)) 1155 topoInit := topology.NewDynamicInitializer(topoOpts) 1156 1157 nodeOpt := BootstrappableTestSetupOptions{ 1158 DisablePeersBootstrapper: true, 1159 FinalBootstrapper: uninitialized.UninitializedTopologyBootstrapperName, 1160 TopologyInitializer: topoInit, 1161 } 1162 1163 nodeOpts := make([]BootstrappableTestSetupOptions, len(instances)) 1164 for i := range instances { 1165 nodeOpts[i] = nodeOpt 1166 } 1167 1168 nodes, closeFn := NewDefaultBootstrappableTestSetups(t, opts, nodeOpts) 1169 1170 nodeClose := func() { // Clean up running servers at end of test 1171 log.Debug("servers closing") 1172 nodes.parallel(func(s TestSetup) { 1173 if s.ServerIsBootstrapped() { 1174 require.NoError(t, s.StopServer()) 1175 } 1176 }) 1177 closeFn() 1178 log.Debug("servers are now down") 1179 } 1180 1181 return nodes, topoInit, nodeClose 1182 } 1183 1184 func mustInspectFilesystem(fsOpts fs.Options) fs.Inspection { 1185 inspection, err := fs.InspectFilesystem(fsOpts) 1186 if err != nil { 1187 panic(err) 1188 } 1189 1190 return inspection 1191 }