-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Custom continuous query options per query rather than per node #5194
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
This makes the following syntax possible: CREATE CONTINUOUS QUERY mycq ON mydb RESAMPLE EVERY 1m FOR 1h BEGIN SELECT mean(value) INTO cpu_mean FROM cpu GROUP BY time(5m) END The RESAMPLE option customizes how often an interval will be sampled and the duration. The interval is customized with EVERY. Any intervals within the resampling duration on a multiple of the resample interval will be updated with the new results from the query. The duration is customized with FOR. This determines how long an interval will participate in resampling. Both options are optional. If RESAMPLE is in the syntax, at least one of the two needs to be given. The default for both is the interval of the continuous query. The service also improves tracking of the last run time and the logic of when a query for an interval should be run. When determining the oldest interval to run for a query, the continuous query service determines what would have been the optimal time to perform the next query based on the last run time. It then uses this time to determine the oldest interval that should be run using the resample duration and will resample all intervals between this time and the current time as opposed to potentially forgetting about the last run in an interval if the continuous query service gets delayed for some reason. This removes the previous config options for customizing continuous queries since they are no longer relevant and adds a new option of customizing the run interval. The run interval determines how often the continuous query service polls for when it should execute a query. This option defaults to 1s, but can be set to 1m if the least common factor of all continuous queries' intervals is a higher value (like 1m).
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1403,6 +1403,13 @@ func (p *Parser) parseCreateContinuousQueryStatement() (*CreateContinuousQuerySt | |
} | ||
stmt.Database = ident | ||
|
||
if p.parseTokenMaybe(RESAMPLE) { | ||
stmt.ResampleEvery, stmt.ResampleFor, err = p.parseResample() | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
// Expect a "BEGIN SELECT" tokens. | ||
if err := p.parseTokens([]Token{BEGIN, SELECT}); err != nil { | ||
return nil, err | ||
|
@@ -2391,6 +2398,47 @@ func (p *Parser) parseCall(name string) (*Call, error) { | |
return &Call{Name: name, Args: args}, nil | ||
} | ||
|
||
// parseResample parses a RESAMPLE [EVERY <duration>] [FOR <duration>]. | ||
// This function assumes RESAMPLE has already been consumed. | ||
// EVERY and FOR are optional, but at least one of the two has to be used. | ||
func (p *Parser) parseResample() (time.Duration, time.Duration, error) { | ||
var interval time.Duration | ||
if p.parseTokenMaybe(EVERY) { | ||
tok, pos, lit := p.scanIgnoreWhitespace() | ||
if tok != DURATION_VAL { | ||
return 0, 0, newParseError(tokstr(tok, lit), []string{"duration"}, pos) | ||
} | ||
|
||
d, err := ParseDuration(lit) | ||
if err != nil { | ||
return 0, 0, &ParseError{Message: err.Error(), Pos: pos} | ||
} | ||
interval = d | ||
} | ||
|
||
var maxDuration time.Duration | ||
if p.parseTokenMaybe(FOR) { | ||
tok, pos, lit := p.scanIgnoreWhitespace() | ||
if tok != DURATION_VAL { | ||
return 0, 0, newParseError(tokstr(tok, lit), []string{"duration"}, pos) | ||
} | ||
|
||
d, err := ParseDuration(lit) | ||
if err != nil { | ||
return 0, 0, &ParseError{Message: err.Error(), Pos: pos} | ||
} | ||
maxDuration = d | ||
} | ||
|
||
// Neither EVERY or FOR were read, so read the next token again | ||
// so we can return a suitable error message. | ||
if interval == 0 && maxDuration == 0 { | ||
tok, pos, lit := p.scanIgnoreWhitespace() | ||
return 0, 0, newParseError(tokstr(tok, lit), []string{"EVERY", "FOR"}, pos) | ||
} | ||
return interval, maxDuration, nil | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefixing variables with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed the |
||
|
||
// scan returns the next token from the underlying scanner. | ||
func (p *Parser) scan() (tok Token, pos Pos, lit string) { return p.s.Scan() } | ||
|
||
|
@@ -2493,6 +2541,17 @@ func (p *Parser) parseTokens(toks []Token) error { | |
return nil | ||
} | ||
|
||
// parseTokenMaybe consumes the next token if it matches the expected one and | ||
// does nothing if the next token is not the next one. | ||
func (p *Parser) parseTokenMaybe(expected Token) bool { | ||
tok, _, _ := p.scanIgnoreWhitespace() | ||
if tok != expected { | ||
p.unscan() | ||
return false | ||
} | ||
return true | ||
} | ||
|
||
// QuoteString returns a quoted string. | ||
func QuoteString(s string) string { | ||
return `'` + strings.NewReplacer("\n", `\n`, `\`, `\\`, `'`, `\'`).Replace(s) + `'` | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1051,7 +1051,7 @@ func TestParser_ParseStatement(t *testing.T) { | |
|
||
// CREATE CONTINUOUS QUERY ... INTO <measurement> | ||
{ | ||
s: `CREATE CONTINUOUS QUERY myquery ON testdb BEGIN SELECT count(field1) INTO measure1 FROM myseries GROUP BY time(5m) END`, | ||
s: `CREATE CONTINUOUS QUERY myquery ON testdb RESAMPLE EVERY 1m FOR 1h BEGIN SELECT count(field1) INTO measure1 FROM myseries GROUP BY time(5m) END`, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should also add tests for just having an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added two more tests that test each possibility of this. |
||
stmt: &influxql.CreateContinuousQueryStatement{ | ||
Name: "myquery", | ||
Database: "testdb", | ||
|
@@ -1070,6 +1070,56 @@ func TestParser_ParseStatement(t *testing.T) { | |
}, | ||
}, | ||
}, | ||
ResampleEvery: time.Minute, | ||
ResampleFor: time.Hour, | ||
}, | ||
}, | ||
|
||
{ | ||
s: `CREATE CONTINUOUS QUERY myquery ON testdb RESAMPLE FOR 1h BEGIN SELECT count(field1) INTO measure1 FROM myseries GROUP BY time(5m) END`, | ||
stmt: &influxql.CreateContinuousQueryStatement{ | ||
Name: "myquery", | ||
Database: "testdb", | ||
Source: &influxql.SelectStatement{ | ||
Fields: []*influxql.Field{{Expr: &influxql.Call{Name: "count", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}}}}}, | ||
Target: &influxql.Target{Measurement: &influxql.Measurement{Name: "measure1", IsTarget: true}}, | ||
Sources: []influxql.Source{&influxql.Measurement{Name: "myseries"}}, | ||
Dimensions: []*influxql.Dimension{ | ||
{ | ||
Expr: &influxql.Call{ | ||
Name: "time", | ||
Args: []influxql.Expr{ | ||
&influxql.DurationLiteral{Val: 5 * time.Minute}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
ResampleFor: time.Hour, | ||
}, | ||
}, | ||
|
||
{ | ||
s: `CREATE CONTINUOUS QUERY myquery ON testdb RESAMPLE EVERY 1m BEGIN SELECT count(field1) INTO measure1 FROM myseries GROUP BY time(5m) END`, | ||
stmt: &influxql.CreateContinuousQueryStatement{ | ||
Name: "myquery", | ||
Database: "testdb", | ||
Source: &influxql.SelectStatement{ | ||
Fields: []*influxql.Field{{Expr: &influxql.Call{Name: "count", Args: []influxql.Expr{&influxql.VarRef{Val: "field1"}}}}}, | ||
Target: &influxql.Target{Measurement: &influxql.Measurement{Name: "measure1", IsTarget: true}}, | ||
Sources: []influxql.Source{&influxql.Measurement{Name: "myseries"}}, | ||
Dimensions: []*influxql.Dimension{ | ||
{ | ||
Expr: &influxql.Call{ | ||
Name: "time", | ||
Args: []influxql.Expr{ | ||
&influxql.DurationLiteral{Val: 5 * time.Minute}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
}, | ||
ResampleEvery: time.Minute, | ||
}, | ||
}, | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why
parseTokenMaybe
here? Instead ofThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed it was a very common pattern in the parser to do your suggestion. While that method works, I was hoping to introduce a new convenience function that does the same thing and reduce some of the bloat and variable shadowing the current method introduces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Like the idea. I think
parseOptionalToken
is a better name.