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

Handle variations in the user_statistics collecting #4306

Merged
merged 8 commits into from
Jun 30, 2018
Merged

Conversation

glinton
Copy link
Contributor

@glinton glinton commented Jun 15, 2018

There are differences in the user_statistics table (if it exists) between MariaDB, Percona, and MySQL. This should allow the stats to be collected properly.

MariaDB has two different table definitions:

Percona has it's own:

MySQL doesn't have the table, but there may be a patch:

Resolves #2910
Closes #3382

Required for all PRs:

  • Signed CLA.
  • Associated README.md updated.
  • Has appropriate unit tests.

@danielnelson danielnelson added area/mysql fix pr to fix corresponding bug labels Jun 18, 2018
@danielnelson danielnelson added this to the 1.7.1 milestone Jun 18, 2018

acc.AddFields("mysql_user_stats", fields, tags)
// disable collecting if table is not found (suppresses repeat errors)
if strings.Contains(err.Error(), "nknown table 'user_statistics'") {
Copy link
Contributor

Choose a reason for hiding this comment

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

typo

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 generally leave out capital letters when matching error strings in case the error changes. (nknown is intended)

Copy link
Contributor

Choose a reason for hiding this comment

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

Okay, personally I would probably just ToLower it, but ideally this type of error matching will be uncommon.

I did find an interesting link off of http://go-database-sql.org/errors.html#identifying-specific-database-errors to a package of error codes for mysql, this might be helpful, though I'm unsure if these work on all mysql flavors.

@@ -790,72 +790,13 @@ func (m *Mysql) gatherGlobalStatuses(db *sql.DB, serv string, acc telegraf.Accum

// gather connection metrics from user_statistics for each user
if m.GatherUserStatistics {
conn_rows, err := db.Query("select user, total_connections, concurrent_connections, connected_time, busy_time, cpu_time, bytes_received, bytes_sent, binlog_bytes_written, rows_fetched, rows_updated, table_rows_read, select_commands, update_commands, other_commands, commit_transactions, rollback_transactions, denied_connections, lost_connections, access_denied, empty_queries, total_ssl_connections FROM INFORMATION_SCHEMA.USER_STATISTICS GROUP BY user")
err = m.GatherUserStatisticsStatuses(db, serv, acc)
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this result in this function being called twice per interval, once from gatherServer and once 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.

It didn't appear to in testing (unless I missed it), but all the logic was already happening at this location anyways.

Copy link
Contributor

Choose a reason for hiding this comment

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

What about L455 though?

}
acc.AddFields("mysql_user_stats", fields, tags)
}
return nil
}

// convertColumns converts selected column names to lowercase.
func convertColumns(s []string, e error) ([]string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe rename columnsToLower or lowerColumns?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great idea

count uint32
)

cols, err := convertColumns(rows.Columns())
Copy link
Contributor

Choose a reason for hiding this comment

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

This is just an observation. I never knew you could pass multiple arguments like this, interesting. I'm not sure I like how it becomes the outer functions job to deal with an upstream error, but I like how it can compress the error handling needed in this function.

"total_ssl_connections": total_ssl_connections,
for i := range cols {
if i == 0 {
continue // skip "user"
Copy link
Contributor

Choose a reason for hiding this comment

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

Percona index 0 comment applies here.

"access_denied": access_denied,
"empty_queries": empty_queries,
"total_ssl_connections": total_ssl_connections,
for i := range cols {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would use for _, column := range cols { and avoid using the index directly. I know in some situations it can be slightly slower to iterate in this manner, but I think it almost always is more readable and less error prone.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In this case, I think leaving it is best. I'll still need the iterator for the read slice and it's pretty clear where cols[i] is only used in the switch block.

continue // skip "user"
}
switch v := read[i].(type) {
case *bool:
Copy link
Contributor

Choose a reason for hiding this comment

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

Only include expected types and the default case can handle anything else.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call. Is it stylistically ok to leave out the default case since it is immediately followed by the function's return?

Copy link
Contributor

Choose a reason for hiding this comment

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

I usually leave the default in to catch any programming mistakes.

if err != nil {
return err
}

tags := map[string]string{"server": servtag, "user": user}
fields := map[string]interface{}{
tags := map[string]string{"server": servtag, "user": *read[0].(*string)}
Copy link
Contributor

Choose a reason for hiding this comment

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

User is not always index 0 (percona)

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'll make it more robust, but in all the docs I checked it would be index 0.

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

@glinton glinton Jun 18, 2018

Choose a reason for hiding this comment

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

Alias for user iirc, though now i'm not sure..

Copy link
Contributor Author

Choose a reason for hiding this comment

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

AH! I was referencing client_statistics table instead of user_statistics table

return err
}

if len(read) != len(cols) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I would probably remove this check, since in theory it is impossible unless there is a programming error and it should. be picked up by the rows.Scan(

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

@loosi loosi mentioned this pull request Jun 19, 2018
3 tasks
@glinton
Copy link
Contributor Author

glinton commented Jun 19, 2018

Closes #1645

@danielnelson danielnelson merged commit 54056f3 into master Jun 30, 2018
@danielnelson danielnelson deleted the bugfix/2910 branch June 30, 2018 01:16
danielnelson pushed a commit that referenced this pull request Jun 30, 2018
codesmith14 pushed a commit to signalfx/telegraf that referenced this pull request Jul 5, 2018
otherpirate pushed a commit to otherpirate/telegraf that referenced this pull request Mar 15, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/mysql fix pr to fix corresponding bug
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants