// Ensures the points writer does not map points beyond the retention policy. func TestPointsWriter_MapShards_Invalid(t *testing.T) { ms := PointsWriterMetaClient{} rp := NewRetentionPolicy("myp", time.Hour, 3) ms.RetentionPolicyFn = func(db, retentionPolicy string) (*meta.RetentionPolicyInfo, error) { return rp, nil } ms.CreateShardGroupIfNotExistsFn = func(database, policy string, timestamp time.Time) (*meta.ShardGroupInfo, error) { return &rp.ShardGroups[0], nil } c := coordinator.NewPointsWriter() c.MetaClient = ms defer c.Close() pr := &coordinator.WritePointsRequest{ Database: "mydb", RetentionPolicy: "myrp", } // Add a point that goes beyond the current retention policy. pr.AddPoint("cpu", 1.0, time.Now().Add(-2*time.Hour), nil) var ( shardMappings *coordinator.ShardMapping err error ) if shardMappings, err = c.MapShards(pr); err != nil { t.Fatalf("unexpected an error: %v", err) } if exp := 0; len(shardMappings.Points) != exp { t.Errorf("MapShards() len mismatch. got %v, exp %v", len(shardMappings.Points), exp) } }
// NewServer returns a new instance of Server built from a config. func NewServer(c *Config, buildInfo *BuildInfo) (*Server, error) { // We need to ensure that a meta directory always exists even if // we don't start the meta store. node.json is always stored under // the meta directory. if err := os.MkdirAll(c.Meta.Dir, 0777); err != nil { return nil, fmt.Errorf("mkdir all: %s", err) } // 0.10-rc1 and prior would sometimes put the node.json at the root // dir which breaks backup/restore and restarting nodes. This moves // the file from the root so it's always under the meta dir. oldPath := filepath.Join(filepath.Dir(c.Meta.Dir), "node.json") newPath := filepath.Join(c.Meta.Dir, "node.json") if _, err := os.Stat(oldPath); err == nil { if err := os.Rename(oldPath, newPath); err != nil { return nil, err } } _, err := influxdb.LoadNode(c.Meta.Dir) if err != nil { if !os.IsNotExist(err) { return nil, err } } if err := raftDBExists(c.Meta.Dir); err != nil { return nil, err } // In 0.10.0 bind-address got moved to the top level. Check // The old location to keep things backwards compatible bind := c.BindAddress s := &Server{ buildInfo: *buildInfo, err: make(chan error), closing: make(chan struct{}), BindAddress: bind, Logger: zap.New( zap.NewTextEncoder(), zap.Output(os.Stderr), ), MetaClient: meta.NewClient(c.Meta), reportingDisabled: c.ReportingDisabled, httpAPIAddr: c.HTTPD.BindAddress, httpUseTLS: c.HTTPD.HTTPSEnabled, tcpAddr: bind, config: c, } s.Monitor = monitor.New(s, c.Monitor) if err := s.MetaClient.Open(); err != nil { return nil, err } s.TSDBStore = tsdb.NewStore(c.Data.Dir) s.TSDBStore.EngineOptions.Config = c.Data // Copy TSDB configuration. s.TSDBStore.EngineOptions.EngineVersion = c.Data.Engine // Create the Subscriber service s.Subscriber = subscriber.NewService(c.Subscriber) // Initialize points writer. s.PointsWriter = coordinator.NewPointsWriter() s.PointsWriter.WriteTimeout = time.Duration(c.Coordinator.WriteTimeout) s.PointsWriter.TSDBStore = s.TSDBStore s.PointsWriter.Subscriber = s.Subscriber // Initialize query executor. s.QueryExecutor = influxql.NewQueryExecutor() s.QueryExecutor.StatementExecutor = &coordinator.StatementExecutor{ MetaClient: s.MetaClient, TaskManager: s.QueryExecutor.TaskManager, TSDBStore: coordinator.LocalTSDBStore{Store: s.TSDBStore}, Monitor: s.Monitor, PointsWriter: s.PointsWriter, MaxSelectPointN: c.Coordinator.MaxSelectPointN, MaxSelectSeriesN: c.Coordinator.MaxSelectSeriesN, MaxSelectBucketsN: c.Coordinator.MaxSelectBucketsN, } s.QueryExecutor.TaskManager.QueryTimeout = time.Duration(c.Coordinator.QueryTimeout) s.QueryExecutor.TaskManager.LogQueriesAfter = time.Duration(c.Coordinator.LogQueriesAfter) s.QueryExecutor.TaskManager.MaxConcurrentQueries = c.Coordinator.MaxConcurrentQueries // Initialize the monitor s.Monitor.Version = s.buildInfo.Version s.Monitor.Commit = s.buildInfo.Commit s.Monitor.Branch = s.buildInfo.Branch s.Monitor.BuildTime = s.buildInfo.Time s.Monitor.PointsWriter = (*monitorPointsWriter)(s.PointsWriter) return s, nil }
func TestPointsWriter_WritePoints(t *testing.T) { tests := []struct { name string database string retentionPolicy string // the responses returned by each shard write call. node ID 1 = pos 0 err []error expErr error }{ { name: "write one success", database: "mydb", retentionPolicy: "myrp", err: []error{nil, nil, nil}, expErr: nil, }, // Write to non-existent database { name: "write to non-existent database", database: "doesnt_exist", retentionPolicy: "", err: []error{nil, nil, nil}, expErr: fmt.Errorf("database not found: doesnt_exist"), }, } for _, test := range tests { pr := &coordinator.WritePointsRequest{ Database: test.database, RetentionPolicy: test.retentionPolicy, } // Three points that range over the shardGroup duration (1h) and should map to two // distinct shards pr.AddPoint("cpu", 1.0, time.Unix(0, 0), nil) pr.AddPoint("cpu", 2.0, time.Unix(0, 0).Add(time.Hour), nil) pr.AddPoint("cpu", 3.0, time.Unix(0, 0).Add(time.Hour+time.Second), nil) // copy to prevent data race theTest := test sm := coordinator.NewShardMapping() sm.MapPoint( &meta.ShardInfo{ID: uint64(1), Owners: []meta.ShardOwner{ {NodeID: 1}, {NodeID: 2}, {NodeID: 3}, }}, pr.Points[0]) sm.MapPoint( &meta.ShardInfo{ID: uint64(2), Owners: []meta.ShardOwner{ {NodeID: 1}, {NodeID: 2}, {NodeID: 3}, }}, pr.Points[1]) sm.MapPoint( &meta.ShardInfo{ID: uint64(2), Owners: []meta.ShardOwner{ {NodeID: 1}, {NodeID: 2}, {NodeID: 3}, }}, pr.Points[2]) // Local coordinator.Node ShardWriter // lock on the write increment since these functions get called in parallel var mu sync.Mutex sw := &fakeShardWriter{ ShardWriteFn: func(shardID, nodeID uint64, points []models.Point) error { mu.Lock() defer mu.Unlock() return theTest.err[int(nodeID)-1] }, } store := &fakeStore{ WriteFn: func(shardID uint64, points []models.Point) error { mu.Lock() defer mu.Unlock() return theTest.err[0] }, } hh := &fakeShardWriter{ ShardWriteFn: func(shardID, nodeID uint64, points []models.Point) error { return nil }, } ms := NewPointsWriterMetaClient() ms.DatabaseFn = func(database string) *meta.DatabaseInfo { return nil } ms.NodeIDFn = func() uint64 { return 1 } subPoints := make(chan *coordinator.WritePointsRequest, 1) sub := Subscriber{} sub.PointsFn = func() chan<- *coordinator.WritePointsRequest { return subPoints } c := coordinator.NewPointsWriter() c.MetaClient = ms c.ShardWriter = sw c.TSDBStore = store c.HintedHandoff = hh c.Subscriber = sub c.Node = &influxdb.Node{ID: 1} c.Open() defer c.Close() err := c.WritePoints(pr.Database, pr.RetentionPolicy, models.ConsistencyLevelOne, pr.Points) if err == nil && test.expErr != nil { t.Errorf("PointsWriter.WritePoints(): '%s' error: got %v, exp %v", test.name, err, test.expErr) } if err != nil && test.expErr == nil { t.Errorf("PointsWriter.WritePoints(): '%s' error: got %v, exp %v", test.name, err, test.expErr) } if err != nil && test.expErr != nil && err.Error() != test.expErr.Error() { t.Errorf("PointsWriter.WritePoints(): '%s' error: got %v, exp %v", test.name, err, test.expErr) } if test.expErr == nil { select { case p := <-subPoints: if !reflect.DeepEqual(p, pr) { t.Errorf("PointsWriter.WritePoints(): '%s' error: unexpected WritePointsRequest got %v, exp %v", test.name, p, pr) } default: t.Errorf("PointsWriter.WritePoints(): '%s' error: Subscriber.Points not called", test.name) } } } }
// Ensures the points writer maps a multiple points across shard group boundaries. func TestPointsWriter_MapShards_Multiple(t *testing.T) { ms := PointsWriterMetaClient{} rp := NewRetentionPolicy("myp", 0, 3) rp.ShardGroupDuration = time.Hour AttachShardGroupInfo(rp, []meta.ShardOwner{ {NodeID: 1}, {NodeID: 2}, {NodeID: 3}, }) AttachShardGroupInfo(rp, []meta.ShardOwner{ {NodeID: 1}, {NodeID: 2}, {NodeID: 3}, }) ms.NodeIDFn = func() uint64 { return 1 } ms.RetentionPolicyFn = func(db, retentionPolicy string) (*meta.RetentionPolicyInfo, error) { return rp, nil } ms.CreateShardGroupIfNotExistsFn = func(database, policy string, timestamp time.Time) (*meta.ShardGroupInfo, error) { for i, sg := range rp.ShardGroups { if timestamp.Equal(sg.StartTime) || timestamp.After(sg.StartTime) && timestamp.Before(sg.EndTime) { return &rp.ShardGroups[i], nil } } panic("should not get here") } c := coordinator.NewPointsWriter() c.MetaClient = ms defer c.Close() pr := &coordinator.WritePointsRequest{ Database: "mydb", RetentionPolicy: "myrp", } // Three points that range over the shardGroup duration (1h) and should map to two // distinct shards pr.AddPoint("cpu", 1.0, time.Unix(0, 0), nil) pr.AddPoint("cpu", 2.0, time.Unix(0, 0).Add(time.Hour), nil) pr.AddPoint("cpu", 3.0, time.Unix(0, 0).Add(time.Hour+time.Second), nil) var ( shardMappings *coordinator.ShardMapping err error ) if shardMappings, err = c.MapShards(pr); err != nil { t.Fatalf("unexpected an error: %v", err) } if exp := 2; len(shardMappings.Points) != exp { t.Errorf("MapShards() len mismatch. got %v, exp %v", len(shardMappings.Points), exp) } for _, points := range shardMappings.Points { // First shard shoud have 1 point w/ first point added if len(points) == 1 && points[0].Time() != pr.Points[0].Time() { t.Fatalf("MapShards() value mismatch. got %v, exp %v", points[0].Time(), pr.Points[0].Time()) } // Second shard shoud have the last two points added if len(points) == 2 && points[0].Time() != pr.Points[1].Time() { t.Fatalf("MapShards() value mismatch. got %v, exp %v", points[0].Time(), pr.Points[1].Time()) } if len(points) == 2 && points[1].Time() != pr.Points[2].Time() { t.Fatalf("MapShards() value mismatch. got %v, exp %v", points[1].Time(), pr.Points[2].Time()) } } }
// Ensures the points writer maps to a new shard group when the shard duration // is changed. func TestPointsWriter_MapShards_AlterShardDuration(t *testing.T) { ms := PointsWriterMetaClient{} rp := NewRetentionPolicy("myp", time.Hour, 3) ms.NodeIDFn = func() uint64 { return 1 } ms.RetentionPolicyFn = func(db, retentionPolicy string) (*meta.RetentionPolicyInfo, error) { return rp, nil } var ( i int now = time.Now() ) ms.CreateShardGroupIfNotExistsFn = func(database, policy string, timestamp time.Time) (*meta.ShardGroupInfo, error) { sg := []meta.ShardGroupInfo{ meta.ShardGroupInfo{ Shards: make([]meta.ShardInfo, 1), StartTime: now, EndTime: now.Add(rp.Duration).Add(-1), }, meta.ShardGroupInfo{ Shards: make([]meta.ShardInfo, 1), StartTime: now.Add(time.Hour), EndTime: now.Add(3 * time.Hour).Add(rp.Duration).Add(-1), }, }[i] i++ return &sg, nil } c := coordinator.NewPointsWriter() c.MetaClient = ms pr := &coordinator.WritePointsRequest{ Database: "mydb", RetentionPolicy: "myrp", } pr.AddPoint("cpu", 1.0, now, nil) pr.AddPoint("cpu", 2.0, now.Add(2*time.Second), nil) var ( shardMappings *coordinator.ShardMapping err error ) if shardMappings, err = c.MapShards(pr); err != nil { t.Fatalf("unexpected an error: %v", err) } if got, exp := len(shardMappings.Points[0]), 2; got != exp { t.Fatalf("got %d point(s), expected %d", got, exp) } if got, exp := len(shardMappings.Shards), 1; got != exp { t.Errorf("got %d shard(s), expected %d", got, exp) } // Now we alter the retention policy duration. rp.ShardGroupDuration = 3 * time.Hour pr = &coordinator.WritePointsRequest{ Database: "mydb", RetentionPolicy: "myrp", } pr.AddPoint("cpu", 1.0, now.Add(2*time.Hour), nil) // Point is beyond previous shard group so a new shard group should be // created. if shardMappings, err = c.MapShards(pr); err != nil { t.Fatalf("unexpected an error: %v", err) } // We can check value of i since it's only incremeneted when a shard group // is created. if got, exp := i, 2; got != exp { t.Fatal("new shard group was not created, expected it to be") } }