Skip to content
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

EVG-5410 Add functions to query execution statistics #1744

Merged
merged 9 commits into from Nov 13, 2018

Conversation

syev
Copy link
Contributor

@syev syev commented Nov 5, 2018

No description provided.

@syev syev requested a review from a team November 5, 2018 21:52
model/stats/db.go Outdated Show resolved Hide resolved
model/stats/db.go Outdated Show resolved Hide resolved
}

// Returns the sort order specification (1, -1) for the date field corresponding to the Sort value.
func sortDateOrder(sort Sort) int {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it make sense to make this a method on Sort?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As this is database specific, I have a preference for leaving it here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what "database specific" means?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value returned, 1 or -1, is destined specifically to be the value of the "date" field in the aggregation pipeline's $sort stage.

)

type GroupBy string
type Sort string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use iotas here? are we using the string form here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string values are not used. We could use iotas as well.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this need to be exported? Do you have a validation method on these types to ensure that they have valid values?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the types are part of the API. The validation is currently done in the validate() method of the StatsFilter but we might want to move it to method directly on these types.

model/stats/query.go Outdated Show resolved Hide resolved
model/stats/query.go Outdated Show resolved Hide resolved
model/stats/query.go Outdated Show resolved Hide resolved
model/stats/query.go Outdated Show resolved Hide resolved
model/stats/query.go Show resolved Hide resolved
if len(f.Tasks) == 0 {
catcher.Add(errors.New("Missing Tasks values"))
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't we avoid needing to pass a bool to the validate function but just inferring from the state of the filter?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bool indicates what the intended use is, I don't think we'd want to infer it from the filter itself. We could include the bool in the filter but we'd have to check that its value matches its usage anyway.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very much in favor of moving the bool out of the function arguments, in whatever way makes the most sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced the bool with two validate methods, one for use with tests stats, one for use with task stats.

model/stats/db.go Outdated Show resolved Hide resolved
fields = []string{"_id.date", "_id.variant", "_id.task_name", "_id.test_file", "_id.distro"}
operators = []string{dateOperator, "$gt", "$gt", "$gt", "$gt"}
values = []interface{}{filter.StartAt.Date, filter.StartAt.BuildVariant, filter.StartAt.Task, filter.StartAt.Test, filter.StartAt.Distro}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there anything that validates that we've specified the right number of arguments to each. It seems really easy to make a change here and get very different semantics. Seems like the operators are "whatever the date operator is plus $gt until the end.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I created a struct to combine the field, operator and value, and now pass a array of the struct.

fields = []string{"_id.date", "_id.variant", "_id.task_name", "_id.distro"}
operators = []string{dateOperator, "$gt", "$gt", "$gt"}
values = []interface{}{filter.StartAt.Date, filter.StartAt.BuildVariant, filter.StartAt.Task, filter.StartAt.Distro}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above comment.

model/stats/db.go Outdated Show resolved Hide resolved
model/stats/query_test.go Outdated Show resolved Hide resolved
suite.Suite
}

func TestStatsQuerySuite(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this suite is probably a little too large: the test cases are very large, and it might be better to have some state on the suite object and then reset it in a setupTest fixture to make it easier to test, and more importantly debug fixtures if they change.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

NumTestFailed int `bson:"num_test_failed" json:"num_test_failed"`
NumSystemFailed int `bson:"num_system_failed" json:"num_system_failed"`
NumSetupFailed int `bson:"num_setup_failed" json:"num_setup_failed"`
AvgDurationSuccess float32 `bson:"avg_duration_success" json:"avg_duration_success"`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mongodb doesn't have 32 bit floats, so lets keep this as float64 just to avoid weirdness.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

model/stats/query.go Show resolved Hide resolved
model/stats/query.go Outdated Show resolved Hide resolved
"date": 1,
"requester": 1,
{"$sort": bson.D{
{Name: "project", Value: 1},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should use ProjectKey, etc., as should the rest of this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// Queries on the precomputed statistics //
///////////////////////////////////////////

// Creates an aggregation pipeline to query test statistics.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments on should be of the form <functionName> ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return id
}

// Edits a match expression to include that the 'fieldName' field must have its value in 'values'.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Done.

// }
//}

func buildMatchArrayExpression(values []string) interface{} {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this return a bson.M?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It can also return a string.

return buildPaginationOrBranches(fields)
}

// Creates an aggregation pipeline to query task statistics.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Comment style for this function and below.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

return catcher.Resolve()
}

// Represents search and aggregation parameters when querying the test or task statistics.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment style is especially important for public types.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

BeforeDate: before,
Tests: tests,
Tasks: tasks,
BuildVariants: nil,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to explicitly set fields that are the zero value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function has been removed.

model/stats/query.go Show resolved Hide resolved

// Queries the precomputed task statistics using a filter.
func GetTaskStats(filter *StatsFilter) ([]TaskStats, error) {
err := filter.validateForTasks()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nil check?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in the validation method.

@@ -147,9 +147,13 @@ db.notifications.ensureIndex({ "sent_at": 1 })

//======hourly_test_stats======//
db.hourly_test_stats.createIndex({ "_id.date": 1 }, { expireAfterSeconds: 26 * 7 * 24 * 3600 }) // 26 weeks TTL
db.hourly_test_stats.createIndex({ "_id.project": 1 }, { "_id.requester": 1}, {"_id.task_name": 1}, {"_id.date": 1})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a ticket open for these indexes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they are in the existing ticket.


type GroupBy string

func (gb *GroupBy) validate() error {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's not correct that these are pointer-recievers. Given that they're strings, I think we just remove some * characteers and this all gets better.

case GroupByTask:
case GroupByTest:
default:
return errors.Errorf("Invalid GroupBy value: %v", *gb)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use %s

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

func GetTaskStats(filter *StatsFilter) ([]TaskStats, error) {
err := filter.validateForTasks()
if err != nil {
return nil, err
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not wrap this error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Yves Duhem added 9 commits November 13, 2018 17:18
Rebase on top of master (fa5b44e).
Split tests into smaller units.
Use struct for pagination fields.
Update validation methods.
Avoid mutable parameter in match stage construction.
Remove NewDefaultStatsFilter constructor
Use float64 instead of float32
Remove json tags
Remove unused code
Update comments to match conventions
Check nil in validateCommon methods
@syev syev merged commit 004db50 into evergreen-ci:master Nov 13, 2018
@syev syev deleted the evg5410 branch December 12, 2018 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants