github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/cmd/services/m3dbnode/main/main_test.go (about) 1 //go:build big 2 // +build big 3 4 // 5 // Copyright (c) 2017 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package main_test 26 27 import ( 28 "fmt" 29 "net/http" 30 "strconv" 31 "sync" 32 "testing" 33 "text/template" 34 "time" 35 36 "github.com/m3db/m3/src/cluster/integration/etcd" 37 "github.com/m3db/m3/src/cluster/placement" 38 "github.com/m3db/m3/src/cluster/services" 39 "github.com/m3db/m3/src/cmd/services/m3dbnode/config" 40 "github.com/m3db/m3/src/dbnode/client" 41 "github.com/m3db/m3/src/dbnode/kvconfig" 42 "github.com/m3db/m3/src/dbnode/server" 43 xconfig "github.com/m3db/m3/src/x/config" 44 "github.com/m3db/m3/src/x/ident" 45 "github.com/m3db/m3/src/x/instrument" 46 xtime "github.com/m3db/m3/src/x/time" 47 48 "github.com/stretchr/testify/assert" 49 "github.com/stretchr/testify/require" 50 "go.uber.org/zap" 51 ) 52 53 // TestConfig tests booting a server using file based configuration. 54 func TestConfig(t *testing.T) { 55 // Embedded kv 56 embeddedKV, err := etcd.New(etcd.NewOptions().SetInitTimeout(30 * time.Second)) 57 require.NoError(t, err) 58 defer func() { 59 require.NoError(t, embeddedKV.Close()) 60 }() 61 require.NoError(t, embeddedKV.Start()) 62 63 // Create config file 64 tmpl, err := template.New("config").Parse(testConfig + kvConfigPortion) 65 require.NoError(t, err) 66 67 configFd, cleanup := tempFile(t, "config.yaml") 68 defer cleanup() 69 70 logFile, cleanupLogFile := tempFileTouch(t, "m3dbnode.log") 71 defer cleanupLogFile() 72 73 configServiceCacheDir, cleanupConfigServiceCacheDir := tempDir(t, "kv") 74 defer cleanupConfigServiceCacheDir() 75 76 dataDir, cleanupDataDir := tempDir(t, "data") 77 defer cleanupDataDir() 78 79 servicePort := nextServicePort() 80 err = tmpl.Execute(configFd, struct { 81 HostID string 82 LogFile string 83 DataDir string 84 ServicePort string 85 ServiceName string 86 ServiceEnv string 87 ServiceZone string 88 ConfigServiceCacheDir string 89 EtcdEndpoints string 90 }{ 91 HostID: hostID, 92 LogFile: logFile, 93 DataDir: dataDir, 94 ServicePort: strconv.Itoa(int(servicePort)), 95 ServiceName: serviceName, 96 ServiceEnv: serviceEnv, 97 ServiceZone: serviceZone, 98 ConfigServiceCacheDir: configServiceCacheDir, 99 EtcdEndpoints: yamlArray(t, embeddedKV.Endpoints()), 100 }) 101 require.NoError(t, err) 102 103 // Setup the placement 104 var cfg config.Configuration 105 err = xconfig.LoadFile(&cfg, configFd.Name(), xconfig.Options{}) 106 require.NoError(t, err) 107 108 discoveryCfg := cfg.DB.DiscoveryOrDefault() 109 envCfg, err := discoveryCfg.EnvironmentConfig(hostID) 110 require.NoError(t, err) 111 112 syncCluster, err := envCfg.Services.SyncCluster() 113 require.NoError(t, err) 114 configSvcClient, err := syncCluster.Service.NewClient(instrument.NewOptions(). 115 SetLogger(zap.NewNop())) 116 require.NoError(t, err) 117 118 svcs, err := configSvcClient.Services(services.NewOverrideOptions()) 119 require.NoError(t, err) 120 121 serviceID := services.NewServiceID(). 122 SetName(serviceName). 123 SetEnvironment(serviceEnv). 124 SetZone(serviceZone) 125 126 metadata := services.NewMetadata(). 127 SetPort(servicePort). 128 SetLivenessInterval(time.Minute). 129 SetHeartbeatInterval(10 * time.Second) 130 131 err = svcs.SetMetadata(serviceID, metadata) 132 require.NoError(t, err) 133 134 placementOpts := placement.NewOptions(). 135 SetValidZone(serviceZone) 136 placementSvc, err := svcs.PlacementService(serviceID, placementOpts) 137 require.NoError(t, err) 138 139 var ( 140 instance = placement.NewInstance(). 141 SetID(hostID). 142 SetEndpoint(endpoint("127.0.0.1", servicePort)). 143 SetPort(servicePort). 144 SetIsolationGroup("local"). 145 SetWeight(1). 146 SetZone(serviceZone) 147 instances = []placement.Instance{instance} 148 // Reduce number of shards to avoid having to tune F.D limits. 149 shards = 4 150 replicas = 1 151 ) 152 153 _, err = placementSvc.BuildInitialPlacement(instances, shards, replicas) 154 require.NoError(t, err) 155 156 // Setup the namespace 157 ns, err := newNamespaceProtoValue(namespaceID) 158 require.NoError(t, err) 159 160 kvStore, err := configSvcClient.KV() 161 require.NoError(t, err) 162 163 _, err = kvStore.Set(kvconfig.NamespacesKey, ns) 164 require.NoError(t, err) 165 166 // Run server 167 var ( 168 interruptCh = make(chan error, 1) 169 bootstrapCh = make(chan struct{}, 1) 170 serverWg sync.WaitGroup 171 ) 172 serverWg.Add(1) 173 go func() { 174 server.Run(server.RunOptions{ 175 ConfigFile: configFd.Name(), 176 BootstrapCh: bootstrapCh, 177 InterruptCh: interruptCh, 178 }) 179 serverWg.Done() 180 }() 181 defer func() { 182 // Resetting DefaultServeMux to prevent multiple assignments 183 // to /debug/dump in Server.Run() 184 http.DefaultServeMux = http.NewServeMux() 185 }() 186 187 // Wait for bootstrap 188 <-bootstrapCh 189 190 // Create client, read and write some data 191 // NB(r): Make sure client config points to the root config 192 // service since we're going to instantiate the client configuration 193 // just by itself. 194 cfg.DB.Client.EnvironmentConfig = &envCfg 195 196 cli, err := cfg.DB.Client.NewClient(client.ConfigurationParameters{}) 197 require.NoError(t, err) 198 199 adminCli := cli.(client.AdminClient) 200 adminSession, err := adminCli.DefaultAdminSession() 201 require.NoError(t, err) 202 defer adminSession.Close() 203 204 // Propagation of shard state from Initializing --> Available post-bootstrap is eventually 205 // consistent, so we must wait. 206 waitUntilAllShardsAreAvailable(t, adminSession) 207 208 // Cast to narrower-interface instead of grabbing DefaultSession to make sure 209 // we use the same topology.Map that we validated in waitUntilAllShardsAreAvailable. 210 session := adminSession.(client.Session) 211 212 start := xtime.Now().Add(-time.Minute) 213 values := []struct { 214 value float64 215 at xtime.UnixNano 216 unit xtime.Unit 217 }{ 218 {value: 1.0, at: start, unit: xtime.Second}, 219 {value: 2.0, at: start.Add(1 * time.Second), unit: xtime.Second}, 220 {value: 3.0, at: start.Add(2 * time.Second), unit: xtime.Second}, 221 } 222 223 for _, v := range values { 224 err := session.Write(ident.StringID(namespaceID), ident.StringID("foo"), v.at, v.value, v.unit, nil) 225 require.NoError(t, err) 226 } 227 228 // Account for first value inserted at xtime.Second precision 229 fetchStart := start.Truncate(time.Second) 230 231 // Account for last value being inserted at xtime.Second and 232 // the "end" param to fetch being exclusive 233 fetchEnd := values[len(values)-1].at.Truncate(time.Second).Add(time.Nanosecond) 234 235 iter, err := session.Fetch(ident.StringID(namespaceID), ident.StringID("foo"), fetchStart, fetchEnd) 236 require.NoError(t, err) 237 238 for _, v := range values { 239 require.True(t, iter.Next()) 240 dp, unit, _ := iter.Current() 241 assert.Equal(t, v.value, dp.Value) 242 // Account for xtime.Second precision on values going in 243 expectAt := v.at.Truncate(time.Second) 244 assert.Equal(t, expectAt, dp.TimestampNanos) 245 assert.Equal(t, v.unit, unit) 246 } 247 248 // Wait for server to stop 249 interruptCh <- fmt.Errorf("test complete") 250 serverWg.Wait() 251 } 252 253 // TestEmbeddedConfig tests booting a server using an embedded KV. 254 func TestEmbeddedConfig(t *testing.T) { 255 // Create config file 256 tmpl, err := template.New("config").Parse(testConfig + embeddedKVConfigPortion) 257 require.NoError(t, err) 258 259 configFd, cleanup := tempFile(t, "config.yaml") 260 defer cleanup() 261 262 logFile, cleanupLogFile := tempFileTouch(t, "m3dbnode.log") 263 defer cleanupLogFile() 264 265 configServiceCacheDir, cleanupConfigServiceCacheDir := tempDir(t, "kv") 266 defer cleanupConfigServiceCacheDir() 267 268 embeddedKVDir, cleanupEmbeddedKVDir := tempDir(t, "embedded") 269 defer cleanupEmbeddedKVDir() 270 271 dataDir, cleanupDataDir := tempDir(t, "data") 272 defer cleanupDataDir() 273 274 servicePort := nextServicePort() 275 err = tmpl.Execute(configFd, struct { 276 HostID string 277 LogFile string 278 DataDir string 279 ServicePort string 280 ServiceName string 281 ServiceEnv string 282 ServiceZone string 283 ConfigServiceCacheDir string 284 EmbeddedKVDir string 285 LPURL string 286 LCURL string 287 APURL string 288 ACURL string 289 EtcdEndpoint string 290 InitialClusterHostID string 291 InitialClusterEndpoint string 292 }{ 293 HostID: hostID, 294 LogFile: logFile, 295 DataDir: dataDir, 296 ServicePort: strconv.Itoa(int(servicePort)), 297 ServiceName: serviceName, 298 ServiceEnv: serviceEnv, 299 ServiceZone: serviceZone, 300 ConfigServiceCacheDir: configServiceCacheDir, 301 EmbeddedKVDir: embeddedKVDir, 302 LPURL: lpURL, 303 LCURL: lcURL, 304 APURL: apURL, 305 ACURL: acURL, 306 EtcdEndpoint: etcdEndpoint, 307 InitialClusterHostID: initialClusterHostID, 308 InitialClusterEndpoint: initialClusterEndpoint, 309 }) 310 require.NoError(t, err) 311 312 // Run server 313 var ( 314 interruptCh = make(chan error, 1) 315 bootstrapCh = make(chan struct{}, 1) 316 embeddedKVCh = make(chan struct{}, 1) 317 serverWg sync.WaitGroup 318 ) 319 serverWg.Add(1) 320 go func() { 321 server.Run(server.RunOptions{ 322 ConfigFile: configFd.Name(), 323 BootstrapCh: bootstrapCh, 324 EmbeddedKVCh: embeddedKVCh, 325 InterruptCh: interruptCh, 326 }) 327 serverWg.Done() 328 }() 329 defer func() { 330 // Resetting DefaultServeMux to prevent multiple assignments 331 // to /debug/dump in Server.Run() 332 http.DefaultServeMux = http.NewServeMux() 333 }() 334 335 // Wait for embedded KV to be up. 336 <-embeddedKVCh 337 338 // Setup the placement 339 var cfg config.Configuration 340 err = xconfig.LoadFile(&cfg, configFd.Name(), xconfig.Options{}) 341 require.NoError(t, err) 342 343 discoveryCfg := cfg.DB.DiscoveryOrDefault() 344 envCfg, err := discoveryCfg.EnvironmentConfig(hostID) 345 require.NoError(t, err) 346 347 syncCluster, err := envCfg.Services.SyncCluster() 348 require.NoError(t, err) 349 configSvcClient, err := syncCluster.Service.NewClient(instrument.NewOptions(). 350 SetLogger(zap.NewNop())) 351 require.NoError(t, err) 352 353 svcs, err := configSvcClient.Services(services.NewOverrideOptions()) 354 require.NoError(t, err) 355 356 serviceID := services.NewServiceID(). 357 SetName(serviceName). 358 SetEnvironment(serviceEnv). 359 SetZone(serviceZone) 360 361 metadata := services.NewMetadata(). 362 SetPort(servicePort). 363 SetLivenessInterval(time.Minute). 364 SetHeartbeatInterval(10 * time.Second) 365 366 err = svcs.SetMetadata(serviceID, metadata) 367 require.NoError(t, err) 368 369 placementOpts := placement.NewOptions(). 370 SetValidZone(serviceZone) 371 placementSvc, err := svcs.PlacementService(serviceID, placementOpts) 372 require.NoError(t, err) 373 374 var ( 375 instance = placement.NewInstance(). 376 SetID(hostID). 377 SetEndpoint(endpoint("127.0.0.1", servicePort)). 378 SetPort(servicePort). 379 SetIsolationGroup("local"). 380 SetWeight(1). 381 SetZone(serviceZone) 382 instances = []placement.Instance{instance} 383 // Use a low number of shards to avoid having to tune F.D limits. 384 shards = 4 385 replicas = 1 386 ) 387 388 _, err = placementSvc.BuildInitialPlacement(instances, shards, replicas) 389 require.NoError(t, err) 390 391 // Setup the namespace 392 ns, err := newNamespaceProtoValue(namespaceID) 393 require.NoError(t, err) 394 395 kvStore, err := configSvcClient.KV() 396 require.NoError(t, err) 397 398 _, err = kvStore.Set(kvconfig.NamespacesKey, ns) 399 require.NoError(t, err) 400 401 // Wait for bootstrap 402 <-bootstrapCh 403 404 // Create client, read and write some data 405 // NB(r): Make sure client config points to the root config 406 // service since we're going to instantiate the client configuration 407 // just by itself. 408 cfg.DB.Client.EnvironmentConfig = &envCfg 409 410 cli, err := cfg.DB.Client.NewClient(client.ConfigurationParameters{}) 411 require.NoError(t, err) 412 413 adminCli := cli.(client.AdminClient) 414 adminSession, err := adminCli.DefaultAdminSession() 415 require.NoError(t, err) 416 defer adminSession.Close() 417 418 // Propagation of shard state from Initializing --> Available post-bootstrap is eventually 419 // consistent, so we must wait. 420 waitUntilAllShardsAreAvailable(t, adminSession) 421 422 // Cast to narrower-interface instead of grabbing DefaultSession to make sure 423 // we use the same topology.Map that we validated in waitUntilAllShardsAreAvailable. 424 session := adminSession.(client.Session) 425 426 start := xtime.Now().Add(-time.Minute) 427 values := []struct { 428 value float64 429 at xtime.UnixNano 430 unit xtime.Unit 431 }{ 432 {value: 1.0, at: start, unit: xtime.Second}, 433 {value: 2.0, at: start.Add(1 * time.Second), unit: xtime.Second}, 434 {value: 3.0, at: start.Add(2 * time.Second), unit: xtime.Second}, 435 } 436 437 for _, v := range values { 438 err := session.Write(ident.StringID(namespaceID), ident.StringID("foo"), v.at, v.value, v.unit, nil) 439 require.NoError(t, err) 440 } 441 442 // Account for first value inserted at xtime.Second precision 443 fetchStart := start.Truncate(time.Second) 444 445 // Account for last value being inserted at xtime.Second and 446 // the "end" param to fetch being exclusive 447 fetchEnd := values[len(values)-1].at.Truncate(time.Second).Add(time.Nanosecond) 448 449 iter, err := session.Fetch(ident.StringID(namespaceID), ident.StringID("foo"), fetchStart, fetchEnd) 450 require.NoError(t, err) 451 452 for _, v := range values { 453 require.True(t, iter.Next()) 454 dp, unit, _ := iter.Current() 455 assert.Equal(t, v.value, dp.Value) 456 // Account for xtime.Second precision on values going in 457 expectAt := v.at.Truncate(time.Second) 458 assert.Equal(t, expectAt, dp.TimestampNanos) 459 assert.Equal(t, v.unit, unit) 460 } 461 462 // Wait for server to stop 463 interruptCh <- fmt.Errorf("test complete") 464 serverWg.Wait() 465 } 466 467 var ( 468 testConfig = ` 469 db: 470 logging: 471 level: info 472 file: {{.LogFile}} 473 474 metrics: 475 prometheus: 476 handlerPath: /metrics 477 listenAddress: 0.0.0.0:9005 478 onError: none 479 sanitization: prometheus 480 samplingRate: 1.0 481 extended: detailed 482 483 listenAddress: 0.0.0.0:{{.ServicePort}} 484 clusterListenAddress: 0.0.0.0:9001 485 httpNodeListenAddress: 0.0.0.0:9002 486 httpClusterListenAddress: 0.0.0.0:9003 487 debugListenAddress: 0.0.0.0:9004 488 489 hostID: 490 resolver: config 491 value: {{.HostID}} 492 493 client: 494 writeConsistencyLevel: majority 495 readConsistencyLevel: unstrict_majority 496 connectConsistencyLevel: any 497 writeTimeout: 10s 498 fetchTimeout: 15s 499 connectTimeout: 20s 500 writeRetry: 501 initialBackoff: 500ms 502 backoffFactor: 3 503 maxRetries: 2 504 jitter: true 505 fetchRetry: 506 initialBackoff: 500ms 507 backoffFactor: 2 508 maxRetries: 3 509 jitter: true 510 backgroundHealthCheckFailLimit: 4 511 backgroundHealthCheckFailThrottleFactor: 0.5 512 513 gcPercentage: 100 514 515 writeNewSeriesAsync: true 516 writeNewSeriesBackoffDuration: 2ms 517 518 commitlog: 519 flushMaxBytes: 524288 520 flushEvery: 1s 521 queue: 522 calculationType: fixed 523 size: 2097152 524 525 filesystem: 526 filePathPrefix: {{.DataDir}} 527 writeBufferSize: 65536 528 dataReadBufferSize: 65536 529 infoReadBufferSize: 128 530 seekReadBufferSize: 4096 531 throughputLimitMbps: 100.0 532 throughputCheckEvery: 128 533 534 repair: 535 enabled: false 536 throttle: 2m 537 checkInterval: 1m 538 539 pooling: 540 blockAllocSize: 16 541 type: simple 542 seriesPool: 543 size: 128 544 lowWatermark: 0.01 545 highWatermark: 0.02 546 blockPool: 547 size: 128 548 lowWatermark: 0.01 549 highWatermark: 0.02 550 encoderPool: 551 size: 128 552 lowWatermark: 0.01 553 highWatermark: 0.02 554 closersPool: 555 size: 128 556 lowWatermark: 0.01 557 highWatermark: 0.02 558 contextPool: 559 size: 128 560 lowWatermark: 0.01 561 highWatermark: 0.02 562 segmentReaderPool: 563 size: 128 564 lowWatermark: 0.01 565 highWatermark: 0.02 566 iteratorPool: 567 size: 128 568 lowWatermark: 0.01 569 highWatermark: 0.02 570 fetchBlockMetadataResultsPool: 571 size: 128 572 capacity: 32 573 lowWatermark: 0.01 574 highWatermark: 0.02 575 fetchBlocksMetadataResultsPool: 576 size: 128 577 capacity: 128 578 lowWatermark: 0.01 579 highWatermark: 0.02 580 replicaMetadataSlicePool: 581 size: 128 582 capacity: 3 583 lowWatermark: 0.01 584 highWatermark: 0.02 585 blockMetadataPool: 586 size: 128 587 lowWatermark: 0.01 588 highWatermark: 0.02 589 blockMetadataSlicePool: 590 size: 128 591 capacity: 32 592 lowWatermark: 0.01 593 highWatermark: 0.02 594 blocksMetadataPool: 595 size: 128 596 lowWatermark: 0.01 597 highWatermark: 0.02 598 blocksMetadataSlicePool: 599 size: 128 600 capacity: 128 601 lowWatermark: 0.01 602 highWatermark: 0.02 603 identifierPool: 604 size: 128 605 lowWatermark: 0.01 606 highWatermark: 0.02 607 bufferBucketPool: 608 size: 128 609 lowWatermark: 0.01 610 highWatermark: 0.02 611 bufferBucketVersionsPool: 612 size: 128 613 lowWatermark: 0.01 614 highWatermark: 0.02 615 bytesPool: 616 buckets: 617 - capacity: 32 618 size: 128 619 - capacity: 512 620 size: 128 621 - capacity: 4096 622 size: 128 623 ` 624 625 kvConfigPortion = ` 626 discovery: 627 config: 628 service: 629 env: {{.ServiceEnv}} 630 zone: {{.ServiceZone}} 631 service: {{.ServiceName}} 632 cacheDir: {{.ConfigServiceCacheDir}} 633 etcdClusters: 634 - zone: {{.ServiceZone}} 635 endpoints: {{.EtcdEndpoints}} 636 autoSyncInterval: -1 637 ` 638 639 embeddedKVConfigPortion = ` 640 discovery: 641 config: 642 service: 643 env: {{.ServiceEnv}} 644 zone: {{.ServiceZone}} 645 service: {{.ServiceName}} 646 cacheDir: {{.ConfigServiceCacheDir}} 647 etcdClusters: 648 - zone: {{.ServiceZone}} 649 endpoints: 650 - {{.EtcdEndpoint}} 651 seedNodes: 652 rootDir: {{.EmbeddedKVDir}} 653 listenPeerUrls: 654 - {{.LPURL}} 655 listenClientUrls: 656 - {{.LCURL}} 657 initialAdvertisePeerUrls: 658 - {{.APURL}} 659 advertiseClientUrls: 660 - {{.ACURL}} 661 initialCluster: 662 - hostID: {{.InitialClusterHostID}} 663 endpoint: {{.InitialClusterEndpoint}} 664 ` 665 )