-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
MS SQL DB metricset #8250
MS SQL DB metricset #8250
Conversation
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.
I have added some comments.
Also as it seems like a potentially big module, I suggest you to start by creating just a metricset, and then continue adding metricsets in follow up PRs, this will help getting this merged.
metricbeat/module/mssql/io/io.go
Outdated
|
||
func (m *MetricSet) loadFileSpaceUsage(db *sql.DB) (common.MapStr, error) { | ||
// Returns the global status, also for versions previous 5.0.2 | ||
rows, err := db.Query("SELECT * FROM sys.dm_db_file_space_usage;") |
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.
You can probably reuse this query between all metricsets, move it to a common place at the module level.
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.
This could probably be a method of a common MetricSet
type.
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.
Moved to a different function to perform queries concurrently, check fb7cc98#diff-333a1912b4f61b216d6a3fd05fd647e7R123 on latest commit
metricbeat/module/mssql/io/data.go
Outdated
"drive_name": c.Str("DriveName"), | ||
"pdw_node_id": c.Int("pdw_node_id"), | ||
}, | ||
//Returns a row for each pending I/O request in SQL Server. |
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.
If there is a different row for each request, then it'd be better to move them to their own metricset, and generate an event for each one of them.
Here you can keep a summary in any case, with the count of pending requests, total pending ticks...
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.
I haven't done this yet on the current commit. I need to investigate a bit more how it works :) but I think I understand the overall idea
metricbeat/module/mssql/io/data.go
Outdated
// fn_virtualfilestats function. | ||
"virtual_file_stats": s.Object{ | ||
"database_name": c.Str("database_name"), | ||
"database_id": c.Int("database_id"), |
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.
Fields that are going to exist in different metricsets should have the same name, this helps on correlation. These database fields are good candidates for that, after applying the schema you can move them to the module level.
Look at some examples that use the ModuleFields
attribute.
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.
after applying the schema you can move them to the module level don't really understand what do you mean here. Can I apply and then modify the schema to move / delete most ocurrences of the same field to a parent s.Object?
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.
Oh, yes, I mean that after applying the schema you can still modify the resulting object, for example deleting some fields after copying them to ModuleFields
.
metricbeat/module/mssql/io/data.go
Outdated
// fn_virtualfilestats function. | ||
"virtual_file_stats": s.Object{ | ||
"database_name": c.Str("database_name"), | ||
"database_id": c.Int("database_id"), |
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.
You could structure these fields in an object:
"database": s.Object{
"name": c.Str("database_name"),
"id": c.Int("database_id"),
}
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.
Because now there is only a single group of metrics in this package, I haven't done it to simplify the tree.
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.
Well, even with an only metricset I think this would be something good to do in any case.
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.
Done in e151780d30f4db3cca88511767a134f295976607
metricbeat/module/mssql/io/io.go
Outdated
mb.BaseMetricSet | ||
counter int | ||
db *sql.DB | ||
} |
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.
The MetricSet
object is probably going to be reusable between metricsets of this module, take a look for example to the mongodb
module where there is a common metricset defined at the module level.
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.
} | ||
}, | ||
"type":"metricsets" | ||
} |
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.
Take a look to the TestData
functions in the integration tests of other modules to see how this file is generated.
metricbeat/module/mssql/mssql.go
Outdated
return &MetricSet{Db: db, BaseMetricSet: base}, nil | ||
} | ||
|
||
func NewModule(base mb.BaseModule) (mb.Module, error) { |
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.
exported function NewModule should have comment or be unexported
Port int `config:"port" validate:"nonzero,required"` | ||
} | ||
|
||
func NewMetricSet(base mb.BaseMetricSet) (*MetricSet, error) { |
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.
exported function NewMetricSet should have comment or be unexported
Db *sql.DB | ||
} | ||
|
||
type Config struct { |
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.
exported type Config should have comment or be unexported
} | ||
} | ||
|
||
type MetricSet struct { |
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.
exported type MetricSet should have comment or be unexported
import ( | ||
"database/sql" | ||
"fmt" | ||
_ "github.com/denisenkom/go-mssqldb" |
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.
a blank import should be only in a main or test package, or have a comment justifying it
import ( | ||
"database/sql" | ||
"fmt" | ||
_ "github.com/denisenkom/go-mssqldb" |
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.
a blank import should be only in a main or test package, or have a comment justifying it
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.
Added some more comments. I think it'd be better if you start by creating just a simple metricset and then continue expanding this metricset and adding new ones in follow up PRs.
metricbeat/module/mssql/db/db.go
Outdated
if c.error == nil { | ||
c.error = err | ||
} else { | ||
c.error = errors.Wrap(c.error, err.Error()) |
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.
I think you are looking for multierror
here, not errors.Wrap
🙂
metricbeat/module/mssql/db/db.go
Outdated
// Fetch methods implements the data gathering and data conversion to the right | ||
// format. It publishes the event which is then forwarded to the output. In case | ||
// of an error set the Error field of mb.Event or simply call report.Error(). | ||
func (m *MetricSet) Fetch() ([]common.MapStr, error) { |
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.
There are multiple interfaces a metricset can implement depending on the signature of its Fetch
method, this one returning the event is the "old" one, we currently prefer the ReportingMetricSetV2
interface, where the Fetch method receives a ReporterV2
object that can be used to send any number of events and errors.
Take a look to its usage in other modules, it'd be nice to use it on new modules like this one.
metricbeat/module/mssql/db/db.go
Outdated
|
||
defer func() { | ||
if closeErr := rows.Close(); closeErr != nil { | ||
//TODO Log error? Ignore it? |
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.
I think that just defer rows.Close()
.
metricbeat/module/mssql/db/db.go
Outdated
c.maprs = append(c.maprs, maprSlice...) | ||
} | ||
|
||
func doQuery(db *sql.DB, query string) ([]common.MapStr, error) { |
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.
If you want this method to be reused between metricsets put it at the module level.
metricbeat/module/mssql/io/io.go
Outdated
return mapR, nil | ||
} | ||
|
||
func rowsToMapR(rows *sql.Rows) (common.MapStr, error) { |
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.
We use to call this method eventMapping
in other modules 🙂
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.
Done b905b46a3a44451bcc1aebe18455431b1667939c
metricbeat/module/mssql/io/io.go
Outdated
// Fetch methods implements the data gathering and data conversion to the right | ||
// format. It publishes the event which is then forwarded to the output. In case | ||
// of an error set the Error field of mb.Event or simply call report.Error(). | ||
func (m *MetricSet) Fetch(report mb.ReporterV2) (common.MapStr, error) { |
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.
Oh, you are already using ReporterV2
here, but fetch here shouldn't return anything, use report.Event()
to send an event and report.Error()
to send an error.
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.
Done here 9c900938162dc77e08b70b1bd8b080b26c7bcd7e, althought I'm a bit confusing now because I'm not fully sure about how to create integration tests
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.
Integration test done here 069cfb5cda5baf8574bf0941241014089cd74db8
metricbeat/module/mssql/db/db.go
Outdated
return nil, err | ||
} | ||
|
||
//TODO Db must be gracefully closed |
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.
Not sure if needed here, you probably want to connect and disconnect on Fetch
But if you want to start something in New
you can implement also the Closer
interface in the MetricSet to do the cleanup when it is stopped.
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.
I was actually thinking how was the lifetime of a metricset and a module
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.
I have seen that you have added a Close
method, but as I said you probably want to connect and disconnect on Fetch
. The main problem of leaving the connection open during all the life of the metricset is that you need to handle reconnections if the connection is closed by the server or any other external reason. This can add unnecessary complexity.
metricbeat/module/mssql/db/db.go
Outdated
"dm_db_persisted_sku_features", //TODO Returns nothing with empty db | ||
"dm_db_session_space_usage", | ||
"dm_db_task_space_usage", | ||
"dm_db_uncontained_entities", //TODO Returns nothing using the driver. Works with sqlcmd |
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.
Are all these queries needed for a simple metricset? :)
metricbeat/module/mssql/fetcher.go
Outdated
"sync" | ||
) | ||
|
||
func NewFetcher(db *sql.DB, qs []string, schema *s.Schema) *fetcher { |
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.
exported function NewFetcher should have comment or be unexported
exported func NewFetcher returns unexported type *mssql.fetcher, which can be annoying to use
package db | ||
|
||
import ( | ||
_ "github.com/denisenkom/go-mssqldb" |
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.
a blank import should be only in a main or test package, or have a comment justifying it
Ok. metricset got really simplified now and the Most of the logic is now in package |
metricbeat/module/mssql/mssql.go
Outdated
return sql.Open("sqlserver", u.String()) | ||
} | ||
|
||
func NewModule(base mb.BaseModule) (mb.Module, error) { |
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.
exported function NewModule should have comment or be unexported
return &MetricSet{BaseMetricSet: base}, nil | ||
} | ||
|
||
func NewDB(config *Config) (*sql.DB, error) { |
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.
exported function NewDB should have comment or be unexported
mb.BaseMetricSet | ||
} | ||
|
||
type Config struct { |
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.
exported type Config should have comment or be unexported
d46192b
to
aef8daa
Compare
Experimental `db` metricset Move methods to call SQL concurrently to the mssql package so it's available to all metricsets Implement io.Close to do cleanup on the db metricset Rename function rowsToMapR to eventsMapping Promote database_id to the parent level within the schema DB connection is created and closed on each Fetch call. Minor improvements on pointer receiver namings on fetcher Updated data.json when launching go tests with -data Added comment to NewDB function. Make private newModule function as it is only used within mssql package but not from any metricset db_integration_test.go rewritten to work with ReporterV2 Removed unused variable and fix typo Remove metricset io from this PR and added license headers Moved files to X-Pack folder Delete references to mssql Squash into a single commit after moved files to x-pack folder
aef8daa
to
6ac24df
Compare
Port int `config:"port" validate:"nonzero,required"` | ||
} | ||
|
||
func NewMetricSet(base mb.BaseMetricSet) (*MetricSet, error) { |
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.
exported function NewMetricSet should have comment or be unexported
mb.BaseMetricSet | ||
} | ||
|
||
type Config struct { |
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.
exported type Config should have comment or be unexported
} | ||
} | ||
|
||
type MetricSet struct { |
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.
exported type MetricSet should have comment or be unexported
import ( | ||
"database/sql" | ||
"fmt" | ||
_ "github.com/denisenkom/go-mssqldb" |
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.
a blank import should be only in a main or test package, or have a comment justifying it
"sync" | ||
) | ||
|
||
func NewFetcher(config *Config, qs []string, schema *s.Schema) (*fetcher, error) { |
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.
exported function NewFetcher should have comment or be unexported
exported func NewFetcher returns unexported type *mssql.fetcher, which can be annoying to use
package db | ||
|
||
import ( | ||
_ "github.com/denisenkom/go-mssqldb" |
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.
a blank import should be only in a main or test package, or have a comment justifying it
package server | ||
|
||
import ( | ||
_ "github.com/denisenkom/go-mssqldb" |
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.
a blank import should be only in a main or test package, or have a comment justifying it
package server | ||
|
||
import ( | ||
_ "github.com/denisenkom/go-mssqldb" |
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.
a blank import should be only in a main or test package, or have a comment justifying it
"github.com/pkg/errors" | ||
) | ||
|
||
func NewFetcher(config *Config, qs []string, schema *s.Schema) (*fetcher, error) { |
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.
exported function NewFetcher should have comment or be unexported
exported func NewFetcher returns unexported type *mssql.fetcher, which can be annoying to use
@sayden I started working on adding magefile.go for building x-pack/metricbeat. You can view that work at https://github.com/andrewkroh/beats/commits/sayden-mssql-metricset. I had to add some dependencies that were missing to With my changes you can do a few things:
I'm working on the integration testing piece, but I can run them with a bit of manual work.
|
@andrewkroh @sayden Perhaps you can collaborate on #8829 ? |
@sayden @ruflin I saw #8829 right after I pushed my commit when I started checking emails. My end goal is to replace most of the shared build logic that exists in Makefiles and python scripts with Go (I don't care so much about porting to Go things that are not shared/reused across Beats like Rather than doing it all at once my approach has been to avoid changing any Makefiles (or other Beats) to the extent that this is possible, re-create any scripts I need with Go, and execute the build with Mage (we started with x-pack/filebeat). Once we prove this is working by being able to build/test/package from x-pack/Metricbeat with Then I think we'll be in a good position to start updating the other Beat's magefiles to remove the dependency on Make. In the end we should be able to fully build and test from the magefile and even on Windows. To that extend I think some of the most important pieces to add to be complete for x-pack/Metricbeat are:
Perhaps we can join a call tomorrow or Friday and discuss how we can collaborate to get your MSSQL module building, testing, and packaged as soon as possible (I know you've been waiting for a while). |
@andrewkroh I think that the approach you describe is the best for all, specially to remove some of the black magic and silent errors we currently have in Python / Makefiles. I have already done the changes needed for formatting and license headers in Makefile / Python so I can start helping you in this with Mage. I'm currently very close to finish I was looking at the |
Continuing here #9202 |
I've started with this particular query https://docs.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/sys-dm-db-file-space-usage-transact-sql?view=sql-server-2017 from the
Database
metricset.I still have to build a local environment to test the entire module. I'll keep pushing progress and request approval once small metricsets are being finished