mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-28 18:42:26 +00:00
160 lines
4.4 KiB
Go
160 lines
4.4 KiB
Go
package clickhouse
|
|
|
|
import (
|
|
"bytes"
|
|
_ "embed"
|
|
"encoding/json"
|
|
"strings"
|
|
"text/template"
|
|
|
|
"github.com/ClickHouse/ClickHouse/programs/diagnostics/internal/collectors"
|
|
"github.com/ClickHouse/ClickHouse/programs/diagnostics/internal/platform"
|
|
"github.com/ClickHouse/ClickHouse/programs/diagnostics/internal/platform/config"
|
|
"github.com/ClickHouse/ClickHouse/programs/diagnostics/internal/platform/data"
|
|
"github.com/Masterminds/semver"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// This collector collects the system db from database
|
|
|
|
type SummaryCollector struct {
|
|
resourceManager *platform.ResourceManager
|
|
}
|
|
|
|
type querySet struct {
|
|
Queries map[string][]query `json:"queries"`
|
|
}
|
|
|
|
type query struct {
|
|
Statement string `json:"statement"`
|
|
Constraint string `json:"constraint"`
|
|
}
|
|
|
|
type ParameterTemplate struct {
|
|
Limit int64
|
|
}
|
|
|
|
//go:embed queries.json
|
|
var queryFile []byte
|
|
|
|
func NewSummaryCollector(m *platform.ResourceManager) *SummaryCollector {
|
|
return &SummaryCollector{
|
|
resourceManager: m,
|
|
}
|
|
}
|
|
|
|
func (sc *SummaryCollector) Collect(conf config.Configuration) (*data.DiagnosticBundle, error) {
|
|
conf, err := conf.ValidateConfig(sc.Configuration())
|
|
if err != nil {
|
|
return &data.DiagnosticBundle{}, err
|
|
}
|
|
var queries querySet
|
|
err = json.Unmarshal(queryFile, &queries)
|
|
if err != nil {
|
|
return &data.DiagnosticBundle{}, errors.Wrap(err, "Unable to read queries from disk")
|
|
}
|
|
limit, err := config.ReadIntValue(conf, "row_limit")
|
|
if err != nil {
|
|
return &data.DiagnosticBundle{}, err
|
|
}
|
|
|
|
paramTemplate := ParameterTemplate{
|
|
Limit: limit,
|
|
}
|
|
frames := make(map[string]data.Frame)
|
|
|
|
serverVersion, err := getServerSemVersion(sc)
|
|
if err != nil {
|
|
return &data.DiagnosticBundle{}, errors.Wrapf(err, "Unable to read server version")
|
|
}
|
|
|
|
var frameErrors []error
|
|
for queryId, sqlQueries := range queries.Queries {
|
|
// we find the first matching query that satisfies the current version. Empty version means ANY version is
|
|
// supported
|
|
for _, sqlQuery := range sqlQueries {
|
|
var queryConstraint *semver.Constraints
|
|
if sqlQuery.Constraint != "" {
|
|
queryConstraint, err = semver.NewConstraint(sqlQuery.Constraint)
|
|
if err != nil {
|
|
//we try another one
|
|
frameErrors = append(frameErrors, errors.Wrapf(err, "Unable to parse version %s for query %s", sqlQuery.Constraint, queryId))
|
|
continue
|
|
}
|
|
}
|
|
if sqlQuery.Constraint == "" || queryConstraint.Check(serverVersion) {
|
|
tmpl, err := template.New(queryId).Parse(sqlQuery.Statement)
|
|
if err != nil {
|
|
frameErrors = append(frameErrors, errors.Wrapf(err, "Unable to parse query %s", queryId))
|
|
//we try another one
|
|
continue
|
|
}
|
|
buf := new(bytes.Buffer)
|
|
err = tmpl.Execute(buf, paramTemplate)
|
|
if err != nil {
|
|
frameErrors = append(frameErrors, errors.Wrapf(err, "Unable to process query %s template", queryId))
|
|
//we try another one
|
|
continue
|
|
}
|
|
frame, err := sc.resourceManager.DbClient.ExecuteStatement(queryId, buf.String())
|
|
if err != nil {
|
|
frameErrors = append(frameErrors, errors.Wrapf(err, "Unable to execute query %s", queryId))
|
|
//we try another one
|
|
} else {
|
|
frames[queryId] = frame
|
|
// only 1 query executed
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
fErrors := data.FrameErrors{
|
|
Errors: frameErrors,
|
|
}
|
|
return &data.DiagnosticBundle{
|
|
Frames: frames,
|
|
Errors: fErrors,
|
|
}, nil
|
|
}
|
|
|
|
func getServerSemVersion(sc *SummaryCollector) (*semver.Version, error) {
|
|
serverVersion, err := sc.resourceManager.DbClient.Version()
|
|
if err != nil {
|
|
return &semver.Version{}, err
|
|
}
|
|
//drop the build number - it is not a semantic version
|
|
versionComponents := strings.Split(serverVersion, ".")
|
|
serverVersion = strings.Join(versionComponents[:len(versionComponents)-1], ".")
|
|
return semver.NewVersion(serverVersion)
|
|
}
|
|
|
|
func (sc *SummaryCollector) Configuration() config.Configuration {
|
|
return config.Configuration{
|
|
Params: []config.ConfigParam{
|
|
config.IntParam{
|
|
Value: 20,
|
|
Param: config.NewParam("row_limit", "Limit rows on supported queries.", false),
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (sc *SummaryCollector) IsDefault() bool {
|
|
return true
|
|
}
|
|
|
|
func (sc *SummaryCollector) Description() string {
|
|
return "Collects summary statistics on the database based on a set of known useful queries."
|
|
}
|
|
|
|
// here we register the collector for use
|
|
func init() {
|
|
collectors.Register("summary", func() (collectors.Collector, error) {
|
|
return &SummaryCollector{
|
|
resourceManager: platform.GetResourceManager(),
|
|
}, nil
|
|
})
|
|
}
|