func (e *StatementExecutor) normalizeMeasurement(m *influxql.Measurement, defaultDatabase string) error { // Targets (measurements in an INTO clause) can have blank names, which means it will be // the same as the measurement name it came from in the FROM clause. if !m.IsTarget && m.Name == "" && m.Regex == nil { return errors.New("invalid measurement") } // Measurement does not have an explicit database? Insert default. if m.Database == "" { m.Database = defaultDatabase } // The database must now be specified by this point. if m.Database == "" { return ErrDatabaseNameRequired } // Find database. di := e.MetaClient.Database(m.Database) if di == nil { return influxdb.ErrDatabaseNotFound(m.Database) } // If no retention policy was specified, use the default. if m.RetentionPolicy == "" { if di.DefaultRetentionPolicy == "" { return fmt.Errorf("default retention policy not set for: %s", di.Name) } m.RetentionPolicy = di.DefaultRetentionPolicy } return nil }
// CreateRetentionPolicy creates a new retention policy on a database. // Returns an error if name is blank or if a database does not exist. func (data *Data) CreateRetentionPolicy(database string, rpi *RetentionPolicyInfo) error { // Validate retention policy. if rpi.Name == "" { return ErrRetentionPolicyNameRequired } else if rpi.ReplicaN < 1 { return ErrReplicationFactorTooLow } // Normalise ShardDuration before comparing to any existing // retention policies rpi.ShardGroupDuration = normalisedShardDuration(rpi.ShardGroupDuration, rpi.Duration) // Find database. di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } else if rp := di.RetentionPolicy(rpi.Name); rp != nil { // RP with that name already exists. Make sure they're the same. if rp.ReplicaN != rpi.ReplicaN || rp.Duration != rpi.Duration || rp.ShardGroupDuration != rpi.ShardGroupDuration { return ErrRetentionPolicyExists } return nil } // Append copy of new policy. rp := RetentionPolicyInfo{ Name: rpi.Name, Duration: rpi.Duration, ReplicaN: rpi.ReplicaN, ShardGroupDuration: rpi.ShardGroupDuration, } di.RetentionPolicies = append(di.RetentionPolicies, rp) return nil }
// CreateContinuousQuery adds a named continuous query to a database. func (data *Data) CreateContinuousQuery(database, name, query string) error { di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } // Ensure the name doesn't already exist. for _, cq := range di.ContinuousQueries { if cq.Name == name { // If the query string is the same, we'll silently return, // otherwise we'll assume the user might be trying to // overwrite an existing CQ with a different query. if strings.ToLower(cq.Query) == strings.ToLower(query) { return nil } return ErrContinuousQueryExists } } // Append new query. di.ContinuousQueries = append(di.ContinuousQueries, ContinuousQueryInfo{ Name: name, Query: query, }) return nil }
// WritePoints writes across multiple local and remote data nodes according the consistency level. func (w *PointsWriter) WritePoints(database, retentionPolicy string, consistencyLevel models.ConsistencyLevel, points []models.Point) error { w.statMap.Add(statWriteReq, 1) w.statMap.Add(statPointWriteReq, int64(len(points))) if retentionPolicy == "" { db := w.MetaClient.Database(database) if db == nil { return influxdb.ErrDatabaseNotFound(database) } retentionPolicy = db.DefaultRetentionPolicy } shardMappings, err := w.MapShards(&WritePointsRequest{Database: database, RetentionPolicy: retentionPolicy, Points: points}) if err != nil { return err } // Write each shard in it's own goroutine and return as soon // as one fails. ch := make(chan error, len(shardMappings.Points)) for shardID, points := range shardMappings.Points { go func(shard *meta.ShardInfo, database, retentionPolicy string, points []models.Point) { ch <- w.writeToShard(shard, database, retentionPolicy, points) }(shardMappings.Shards[shardID], database, retentionPolicy, points) } // Send points to subscriptions if possible. ok := false // We need to lock just in case the channel is about to be nil'ed w.mu.RLock() select { case w.subPoints <- &WritePointsRequest{Database: database, RetentionPolicy: retentionPolicy, Points: points}: ok = true default: } w.mu.RUnlock() if ok { w.statMap.Add(statSubWriteOK, 1) } else { w.statMap.Add(statSubWriteDrop, 1) } timeout := time.NewTimer(w.WriteTimeout) defer timeout.Stop() for range shardMappings.Points { select { case <-w.closing: return ErrWriteFailed case <-timeout.C: w.statMap.Add(statWriteTimeout, 1) // return timeout error to caller return ErrTimeout case err := <-ch: if err != nil { return err } } } return nil }
// CreateRetentionPolicy creates a new retention policy on a database. // Returns an error if name is blank or if a database does not exist. func (data *Data) CreateRetentionPolicy(database string, rpi *RetentionPolicyInfo) error { // Validate retention policy. if rpi.Name == "" { return ErrRetentionPolicyNameRequired } else if rpi.ReplicaN < 1 { return ErrReplicationFactorTooLow } // Find database. di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } else if rp := di.RetentionPolicy(rpi.Name); rp != nil { // RP with that name already exists. Make sure they're the same. if rp.ReplicaN != rpi.ReplicaN || rp.Duration != rpi.Duration { return ErrRetentionPolicyExists } return nil } // Append new policy. di.RetentionPolicies = append(di.RetentionPolicies, RetentionPolicyInfo{ Name: rpi.Name, Duration: rpi.Duration, ShardGroupDuration: shardGroupDuration(rpi.Duration), ReplicaN: rpi.ReplicaN, }) return nil }
// writeDatabaseInfo will write the relative paths of all shards in the database on // this server into the connection func (s *Service) writeDatabaseInfo(conn net.Conn, database string) error { res := Response{} db, err := s.MetaClient.Database(database) if err != nil { return err } if db == nil { return influxdb.ErrDatabaseNotFound(database) } for _, rp := range db.RetentionPolicies { for _, sg := range rp.ShardGroups { for _, sh := range sg.Shards { // ignore if the shard isn't on the server if s.TSDBStore.Shard(sh.ID) == nil { continue } path, err := s.TSDBStore.ShardRelativePath(sh.ID) if err != nil { return err } res.Paths = append(res.Paths, path) } } } if err := json.NewEncoder(conn).Encode(res); err != nil { return fmt.Errorf("encode resonse: %s", err.Error()) } return nil }
// CreateRetentionPolicy creates a new retention policy on a database. // Returns an error if name is blank or if a database does not exist. func (data *Data) CreateRetentionPolicy(database string, rpi *RetentionPolicyInfo) error { // Validate retention policy. if rpi == nil { return ErrRetentionPolicyRequired } else if rpi.Name == "" { return ErrRetentionPolicyNameRequired } else if rpi.ReplicaN < 1 { return ErrReplicationFactorTooLow } // Normalise ShardDuration before comparing to any existing // retention policies. The client is supposed to do this, but // do it again to verify input. rpi.ShardGroupDuration = normalisedShardDuration(rpi.ShardGroupDuration, rpi.Duration) if rpi.Duration > 0 && rpi.Duration < rpi.ShardGroupDuration { return ErrIncompatibleDurations } // Find database. di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } else if rp := di.RetentionPolicy(rpi.Name); rp != nil { // RP with that name already exists. Make sure they're the same. if rp.ReplicaN != rpi.ReplicaN || rp.Duration != rpi.Duration || rp.ShardGroupDuration != rpi.ShardGroupDuration { return ErrRetentionPolicyExists } return nil } // Append copy of new policy. di.RetentionPolicies = append(di.RetentionPolicies, *rpi) return nil }
func TestMetaService_Subscriptions_Drop(t *testing.T) { t.Parallel() d, s, c := newServiceAndClient() defer os.RemoveAll(d) defer s.Close() defer c.Close() // Create a database to use if res := c.ExecuteStatement(mustParseStatement("CREATE DATABASE db0")); res.Err != nil { t.Fatal(res.Err) } db, err := c.Database("db0") if err != nil { t.Fatal(err) } else if db.Name != "db0" { t.Fatalf("db name wrong: %s", db.Name) } // DROP SUBSCRIPTION returns ErrSubscriptionNotFound when the // subscription is unknown. res := c.ExecuteStatement(mustParseStatement(`DROP SUBSCRIPTION foo ON db0."default"`)) if got, exp := res.Err, meta.ErrSubscriptionNotFound; got.Error() != exp.Error() { t.Fatalf("got: %s, exp: %s", got, exp) } // Create a subscription. if res = c.ExecuteStatement(mustParseStatement(`CREATE SUBSCRIPTION sub0 ON db0."default" DESTINATIONS ALL 'udp://example.com:9090'`)); res.Err != nil { t.Fatal(res.Err) } // DROP SUBSCRIPTION returns an influxdb.ErrDatabaseNotFound when // the database is unknown. res = c.ExecuteStatement(mustParseStatement(`DROP SUBSCRIPTION sub0 ON foo."default"`)) if got, exp := res.Err, influxdb.ErrDatabaseNotFound("foo"); got.Error() != exp.Error() { t.Fatalf("got: %s, exp: %s", got, exp) } // DROP SUBSCRIPTION returns an influxdb.ErrRetentionPolicyNotFound // when the retention policy is unknown. res = c.ExecuteStatement(mustParseStatement(`DROP SUBSCRIPTION sub0 ON db0."foo_policy"`)) if got, exp := res.Err, influxdb.ErrRetentionPolicyNotFound("foo_policy"); got.Error() != exp.Error() { t.Fatalf("got: %s, exp: %s", got, exp) } // DROP SUBSCRIPTION drops the subsciption if it can find it. res = c.ExecuteStatement(mustParseStatement(`DROP SUBSCRIPTION sub0 ON db0."default"`)) if got := res.Err; got != nil { t.Fatalf("got: %s, exp: %v", got, nil) } if res = c.ExecuteStatement(mustParseStatement(`SHOW SUBSCRIPTIONS`)); res.Err != nil { t.Fatal(res.Err) } else if got, exp := len(res.Series), 0; got != exp { t.Fatalf("got %d series, expected %d", got, exp) } }
// DropDatabase removes a database by name. func (data *Data) DropDatabase(name string) error { for i := range data.Databases { if data.Databases[i].Name == name { data.Databases = append(data.Databases[:i], data.Databases[i+1:]...) return nil } } return influxdb.ErrDatabaseNotFound(name) }
// RetentionPolicy returns the requested retention policy info. func (c *Client) RetentionPolicy(database, name string) (rpi *RetentionPolicyInfo, err error) { c.mu.RLock() defer c.mu.RUnlock() db := c.cacheData.Database(database) if db == nil { return nil, influxdb.ErrDatabaseNotFound(database) } return db.RetentionPolicy(name), nil }
func (e *StatementExecutor) executeShowRetentionPoliciesStatement(q *influxql.ShowRetentionPoliciesStatement) (models.Rows, error) { di := e.MetaClient.Database(q.Database) if di == nil { return nil, influxdb.ErrDatabaseNotFound(q.Database) } row := &models.Row{Columns: []string{"name", "duration", "shardGroupDuration", "replicaN", "default"}} for _, rpi := range di.RetentionPolicies { row.Values = append(row.Values, []interface{}{rpi.Name, rpi.Duration.String(), rpi.ShardGroupDuration.String(), rpi.ReplicaN, di.DefaultRetentionPolicy == rpi.Name}) } return []*models.Row{row}, nil }
// UpdateRetentionPolicy updates an existing retention policy. func (data *Data) UpdateRetentionPolicy(database, name string, rpu *RetentionPolicyUpdate, makeDefault bool) error { // Find database. di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } // Find policy. rpi := di.RetentionPolicy(name) if rpi == nil { return influxdb.ErrRetentionPolicyNotFound(name) } // Ensure new policy doesn't match an existing policy. if rpu.Name != nil && *rpu.Name != name && di.RetentionPolicy(*rpu.Name) != nil { return ErrRetentionPolicyNameExists } // Enforce duration of at least MinRetentionPolicyDuration if rpu.Duration != nil && *rpu.Duration < MinRetentionPolicyDuration && *rpu.Duration != 0 { return ErrRetentionPolicyDurationTooLow } // Enforce duration is at least the shard duration if (rpu.Duration != nil && *rpu.Duration > 0 && ((rpu.ShardGroupDuration != nil && *rpu.Duration < *rpu.ShardGroupDuration) || (rpu.ShardGroupDuration == nil && *rpu.Duration < rpi.ShardGroupDuration))) || (rpu.Duration == nil && rpi.Duration > 0 && rpu.ShardGroupDuration != nil && rpi.Duration < *rpu.ShardGroupDuration) { return ErrIncompatibleDurations } // Update fields. if rpu.Name != nil { rpi.Name = *rpu.Name } if rpu.Duration != nil { rpi.Duration = *rpu.Duration } if rpu.ReplicaN != nil { rpi.ReplicaN = *rpu.ReplicaN } if rpu.ShardGroupDuration != nil { rpi.ShardGroupDuration = normalisedShardDuration(*rpu.ShardGroupDuration, rpi.Duration) } if di.DefaultRetentionPolicy != rpi.Name && makeDefault { di.DefaultRetentionPolicy = rpi.Name } return nil }
// RetentionPolicy returns a retention policy for a database by name. func (data *Data) RetentionPolicy(database, name string) (*RetentionPolicyInfo, error) { di := data.Database(database) if di == nil { return nil, influxdb.ErrDatabaseNotFound(database) } for i := range di.RetentionPolicies { if di.RetentionPolicies[i].Name == name { return &di.RetentionPolicies[i], nil } } return nil, nil }
// Database returns info for the requested database. func (c *Client) Database(name string) (*DatabaseInfo, error) { c.mu.RLock() data := c.cacheData.Clone() c.mu.RUnlock() for _, d := range data.Databases { if d.Name == name { return &d, nil } } return nil, influxdb.ErrDatabaseNotFound(name) }
// RetentionPolicy returns the requested retention policy info. func (c *Client) RetentionPolicy(database, name string) (rpi *RetentionPolicyInfo, err error) { db, err := c.Database(database) if err != nil { return nil, err } // TODO: This should not be handled here if db == nil { return nil, influxdb.ErrDatabaseNotFound(database) } return db.RetentionPolicy(name), nil }
func (e *StatementExecutor) executeShowRetentionPoliciesStatement(q *influxql.ShowRetentionPoliciesStatement) *influxql.Result { di, err := e.Store.Database(q.Database) if err != nil { return &influxql.Result{Err: err} } else if di == nil { return &influxql.Result{Err: influxdb.ErrDatabaseNotFound(q.Database)} } row := &models.Row{Columns: []string{"name", "duration", "replicaN", "default"}} for _, rpi := range di.RetentionPolicies { row.Values = append(row.Values, []interface{}{rpi.Name, rpi.Duration.String(), rpi.ReplicaN, di.DefaultRetentionPolicy == rpi.Name}) } return &influxql.Result{Series: []*models.Row{row}} }
// DropContinuousQuery removes a continuous query. func (data *Data) DropContinuousQuery(database, name string) error { di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } for i := range di.ContinuousQueries { if di.ContinuousQueries[i].Name == name { di.ContinuousQueries = append(di.ContinuousQueries[:i], di.ContinuousQueries[i+1:]...) return nil } } return ErrContinuousQueryNotFound }
// SetDefaultRetentionPolicy sets the default retention policy for a database. func (data *Data) SetDefaultRetentionPolicy(database, name string) error { // Find database and verify policy exists. di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } else if di.RetentionPolicy(name) == nil { return influxdb.ErrRetentionPolicyNotFound(name) } // Set default policy. di.DefaultRetentionPolicy = name return nil }
// DropRetentionPolicy removes a retention policy from a database by name. func (data *Data) DropRetentionPolicy(database, name string) error { // Find database. di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } // Remove from list. for i := range di.RetentionPolicies { if di.RetentionPolicies[i].Name == name { di.RetentionPolicies = append(di.RetentionPolicies[:i], di.RetentionPolicies[i+1:]...) break } } return nil }
// CreateRetentionPolicy creates a new retention policy on a database. // Returns an error if name is blank or if a database does not exist. func (data *Data) CreateRetentionPolicy(database string, rpi *RetentionPolicyInfo, makeDefault bool) error { // Validate retention policy. if rpi == nil { return ErrRetentionPolicyRequired } else if rpi.Name == "" { return ErrRetentionPolicyNameRequired } else if rpi.ReplicaN < 1 { return ErrReplicationFactorTooLow } // Normalise ShardDuration before comparing to any existing // retention policies. The client is supposed to do this, but // do it again to verify input. rpi.ShardGroupDuration = normalisedShardDuration(rpi.ShardGroupDuration, rpi.Duration) if rpi.Duration > 0 && rpi.Duration < rpi.ShardGroupDuration { return ErrIncompatibleDurations } // Find database. di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } else if rp := di.RetentionPolicy(rpi.Name); rp != nil { // RP with that name already exists. Make sure they're the same. if rp.ReplicaN != rpi.ReplicaN || rp.Duration != rpi.Duration || rp.ShardGroupDuration != rpi.ShardGroupDuration { return ErrRetentionPolicyExists } // if they want to make it default, and it's not the default, it's not an identical command so it's an error if makeDefault && di.DefaultRetentionPolicy != rpi.Name { return ErrRetentionPolicyConflict } return nil } // Append copy of new policy. di.RetentionPolicies = append(di.RetentionPolicies, *rpi) // Set the default if needed if makeDefault { di.DefaultRetentionPolicy = rpi.Name } return nil }
func TestMetaService_Subscriptions_Drop(t *testing.T) { t.Parallel() d, s, c := newServiceAndClient() defer os.RemoveAll(d) defer s.Close() defer c.Close() // Create a database to use if _, err := c.CreateDatabase("db0"); err != nil { t.Fatal(err) } // DROP SUBSCRIPTION returns ErrSubscriptionNotFound when the // subscription is unknown. err := c.DropSubscription("db0", "default", "foo") if got, exp := err, meta.ErrSubscriptionNotFound; got.Error() != exp.Error() { t.Fatalf("got: %s, exp: %s", got, exp) } // Create a subscription. if err := c.CreateSubscription("db0", "default", "sub0", "ALL", []string{"udp://example.com:9090"}); err != nil { t.Fatal(err) } // DROP SUBSCRIPTION returns an influxdb.ErrDatabaseNotFound when // the database is unknown. err = c.DropSubscription("foo", "default", "sub0") if got, exp := err, influxdb.ErrDatabaseNotFound("foo"); got.Error() != exp.Error() { t.Fatalf("got: %s, exp: %s", got, exp) } // DROP SUBSCRIPTION returns an influxdb.ErrRetentionPolicyNotFound // when the retention policy is unknown. err = c.DropSubscription("db0", "foo_policy", "sub0") if got, exp := err, influxdb.ErrRetentionPolicyNotFound("foo_policy"); got.Error() != exp.Error() { t.Fatalf("got: %s, exp: %s", got, exp) } // DROP SUBSCRIPTION drops the subsciption if it can find it. err = c.DropSubscription("db0", "default", "sub0") if got := err; got != nil { t.Fatalf("got: %s, exp: %v", got, nil) } }
// UpdateRetentionPolicy updates an existing retention policy. func (data *Data) UpdateRetentionPolicy(database, name string, rpu *RetentionPolicyUpdate) error { // Find database. di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } // Find policy. rpi := di.RetentionPolicy(name) if rpi == nil { return influxdb.ErrRetentionPolicyNotFound(name) } // Ensure new policy doesn't match an existing policy. if rpu.Name != nil && *rpu.Name != name && di.RetentionPolicy(*rpu.Name) != nil { return ErrRetentionPolicyNameExists } // Enforce duration of at least MinRetentionPolicyDuration if rpu.Duration != nil && *rpu.Duration < MinRetentionPolicyDuration && *rpu.Duration != 0 { return ErrRetentionPolicyDurationTooLow } // Update fields. if rpu.Name != nil { rpi.Name = *rpu.Name } if rpu.Duration != nil { rpi.Duration = *rpu.Duration } if rpu.ReplicaN != nil { rpi.ReplicaN = *rpu.ReplicaN } if rpu.ShardGroupDuration != nil { rpi.ShardGroupDuration = *rpu.ShardGroupDuration } else { rpi.ShardGroupDuration = shardGroupDuration(rpi.Duration) } return nil }
// CreateContinuousQuery adds a named continuous query to a database. func (data *Data) CreateContinuousQuery(database, name, query string) error { di := data.Database(database) if di == nil { return influxdb.ErrDatabaseNotFound(database) } // Ensure the name doesn't already exist. for i := range di.ContinuousQueries { if di.ContinuousQueries[i].Name == name { return ErrContinuousQueryExists } } // Append new query. di.ContinuousQueries = append(di.ContinuousQueries, ContinuousQueryInfo{ Name: name, Query: query, }) return nil }