mirror of
https://github.com/hanzoai/o11y.git
synced 2026-10-15 12:58:33 +00:00
feat(o11y): kill -tags signoz + -tags otlp + -tags grpc, rename clickhouse → datastore
No more opt-in tag escape hatches — ZAP is the only path.
Deleted:
pkg/prometheus/clickhouseprometheus/ (was -tags signoz, PromQL/ClickHouse adapter)
pkg/prometheus/prometheustest/ (was -tags signoz, test provider)
pkg/query-service/rules/managertestfactory.go (was -tags signoz)
pkg/query-service/app/logparsingpipeline/preview.go → preview_noop.go promoted to canonical
pkg/query-service/app/opamp/{otelconfig/,configure_ingestionRules.go} (was -tags grpc)
luxfi/trace:
exporter_otlp.go (was -tags otlp), exporter_grpc.go (was -tags grpc),
exporter_noop.go all deleted. tracer.go simplified: Type=ZAP returns
real exporter, everything else returns Noop.
Rename clickhouse → datastore (mirrors github.com/hanzoai/datastore brand):
- dirs: pkg/telemetrystore/clickhousetelemetrystore → datastoretelemetrystore
pkg/query-service/app/clickhouseReader → datastoreReader
pkg/variables/clickhouse → datastore
pkg/query-service/app/integrations/builtin_integrations/clickhouse → datastore
- 151 files mass-renamed: Clickhouse → Datastore, clickhouse → datastore
- External module paths preserved: github.com/ClickHouse/clickhouse-go/v2 (aliased as 'datastore')
AfterShip/clickhouse-sql-parser, srikanthccv/ClickHouse-go-mock — names unchanged
- External API surface preserved: sqlbuilder.ClickHouse (huandu/go-sqlbuilder enum)
Default-tag dep audit: 0 grpc, 0 grpc-gateway, 0 google api, 0 cloud auth, 0 s2a-go.
Build tags landed: NONE in o11y. ZAP-native is the only path.
This commit is contained in:
@@ -121,7 +121,7 @@ func runServer(ctx context.Context, config o11y.Config, logger *slog.Logger) err
|
||||
"spans", len(b.Spans),
|
||||
)
|
||||
// TODO(zap-ingest): write batch.Spans into telemetrystore.TelemetryStore
|
||||
// once the SpanBatch → ClickHouse adapter lands.
|
||||
// once the SpanBatch → Datastore adapter lands.
|
||||
return nil
|
||||
},
|
||||
})
|
||||
|
||||
@@ -146,7 +146,7 @@ func (df *DeprecatedFlags) RegisterFlags(cmd *cobra.Command) {
|
||||
_ = cmd.Flags().MarkDeprecated("config", "use O11Y_PROMETHEUS_CONFIG instead")
|
||||
_ = cmd.Flags().MarkDeprecated("flux-interval", "use O11Y_QUERIER_FLUX__INTERVAL instead")
|
||||
_ = cmd.Flags().MarkDeprecated("flux-interval-for-trace-detail", "use O11Y_QUERIER_FLUX__INTERVAL instead")
|
||||
_ = cmd.Flags().MarkDeprecated("cluster", "use O11Y_TELEMETRYSTORE_CLICKHOUSE_CLUSTER instead")
|
||||
_ = cmd.Flags().MarkDeprecated("cluster", "use O11Y_TELEMETRYSTORE_DATASTORE_CLUSTER instead")
|
||||
_ = cmd.Flags().MarkDeprecated("prefer-span-metrics", "use O11Y_FLAGGER_CONFIG_BOOLEAN_USE__SPAN__METRICS instead")
|
||||
_ = cmd.Flags().MarkDeprecated("gateway-url", "use O11Y_GATEWAY_URL instead")
|
||||
}
|
||||
@@ -245,9 +245,9 @@ func mergeAndEnsureBackwardCompatibility(ctx context.Context, logger *slog.Logge
|
||||
config.TelemetryStore.Provider = os.Getenv("STORAGE")
|
||||
}
|
||||
|
||||
if os.Getenv("ClickHouseUrl") != "" {
|
||||
logger.WarnContext(ctx, "[Deprecated] env ClickHouseUrl is deprecated and scheduled for removal. Please use O11Y_TELEMETRYSTORE_CLICKHOUSE_DSN instead.")
|
||||
config.TelemetryStore.Clickhouse.DSN = os.Getenv("ClickHouseUrl")
|
||||
if os.Getenv("DatastoreUrl") != "" {
|
||||
logger.WarnContext(ctx, "[Deprecated] env DatastoreUrl is deprecated and scheduled for removal. Please use O11Y_TELEMETRYSTORE_DATASTORE_DSN instead.")
|
||||
config.TelemetryStore.Clickhouse.DSN = os.Getenv("DatastoreUrl")
|
||||
}
|
||||
|
||||
if deprecatedFlags.MaxIdleConns != 50 {
|
||||
@@ -332,7 +332,7 @@ func mergeAndEnsureBackwardCompatibility(ctx context.Context, logger *slog.Logge
|
||||
}
|
||||
|
||||
if deprecatedFlags.Cluster != "" {
|
||||
logger.WarnContext(ctx, "[Deprecated] flag --cluster is deprecated and scheduled for removal. Please use O11Y_TELEMETRYSTORE_CLICKHOUSE_CLUSTER instead.")
|
||||
logger.WarnContext(ctx, "[Deprecated] flag --cluster is deprecated and scheduled for removal. Please use O11Y_TELEMETRYSTORE_DATASTORE_CLUSTER instead.")
|
||||
config.TelemetryStore.Clickhouse.Cluster = deprecatedFlags.Cluster
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ func (provider *provider) addQuerierRoutes(router *mux.Router) error {
|
||||
ID: "QueryRangeV5",
|
||||
Tags: []string{"querier"},
|
||||
Summary: "Query range",
|
||||
Description: "Execute a composite query over a time range. Supports builder queries (traces, logs, metrics), formulas, trace operators, PromQL, and ClickHouse SQL.",
|
||||
Description: "Execute a composite query over a time range. Supports builder queries (traces, logs, metrics), formulas, trace operators, PromQL, and Datastore SQL.",
|
||||
Request: new(qbtypes.QueryRangeRequest),
|
||||
RequestContentType: "application/json",
|
||||
RequestExamples: []handler.OpenAPIExample{
|
||||
@@ -355,8 +355,8 @@ func (provider *provider) addQuerierRoutes(router *mux.Router) error {
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "clickhouse_sql_traces_time_series",
|
||||
Summary: "ClickHouse SQL: traces time series with resource filter",
|
||||
Name: "datastore_sql_traces_time_series",
|
||||
Summary: "Datastore SQL: traces time series with resource filter",
|
||||
Value: map[string]any{
|
||||
"schemaVersion": "v1",
|
||||
"start": 1640995200000,
|
||||
@@ -365,7 +365,7 @@ func (provider *provider) addQuerierRoutes(router *mux.Router) error {
|
||||
"compositeQuery": map[string]any{
|
||||
"queries": []any{
|
||||
map[string]any{
|
||||
"type": "clickhouse_sql",
|
||||
"type": "datastore_sql",
|
||||
"spec": map[string]any{
|
||||
"name": "span_rate",
|
||||
"query": "WITH __resource_filter AS (" +
|
||||
@@ -384,8 +384,8 @@ func (provider *provider) addQuerierRoutes(router *mux.Router) error {
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "clickhouse_sql_logs_raw",
|
||||
Summary: "ClickHouse SQL: raw logs with resource filter",
|
||||
Name: "datastore_sql_logs_raw",
|
||||
Summary: "Datastore SQL: raw logs with resource filter",
|
||||
Value: map[string]any{
|
||||
"schemaVersion": "v1",
|
||||
"start": 1640995200000,
|
||||
@@ -394,7 +394,7 @@ func (provider *provider) addQuerierRoutes(router *mux.Router) error {
|
||||
"compositeQuery": map[string]any{
|
||||
"queries": []any{
|
||||
map[string]any{
|
||||
"type": "clickhouse_sql",
|
||||
"type": "datastore_sql",
|
||||
"spec": map[string]any{
|
||||
"name": "recent_errors",
|
||||
"query": "WITH __resource_filter AS (" +
|
||||
@@ -414,8 +414,8 @@ func (provider *provider) addQuerierRoutes(router *mux.Router) error {
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "clickhouse_sql_traces_scalar",
|
||||
Summary: "ClickHouse SQL: scalar aggregate with resource filter",
|
||||
Name: "datastore_sql_traces_scalar",
|
||||
Summary: "Datastore SQL: scalar aggregate with resource filter",
|
||||
Value: map[string]any{
|
||||
"schemaVersion": "v1",
|
||||
"start": 1640995200000,
|
||||
@@ -424,7 +424,7 @@ func (provider *provider) addQuerierRoutes(router *mux.Router) error {
|
||||
"compositeQuery": map[string]any{
|
||||
"queries": []any{
|
||||
map[string]any{
|
||||
"type": "clickhouse_sql",
|
||||
"type": "datastore_sql",
|
||||
"spec": map[string]any{
|
||||
"name": "total_spans",
|
||||
"query": "WITH __resource_filter AS (" +
|
||||
|
||||
@@ -175,7 +175,7 @@ func (module *module) GetByMetricNames(ctx context.Context, orgID valuer.UUID, m
|
||||
|
||||
// Check all three query types
|
||||
module.checkBuilderQueriesForMetricNames(query, metricNames, foundMetrics)
|
||||
module.checkClickHouseQueriesForMetricNames(ctx, query, metricNames, foundMetrics)
|
||||
module.checkDatastoreQueriesForMetricNames(ctx, query, metricNames, foundMetrics)
|
||||
module.checkPromQLQueriesForMetricNames(ctx, query, metricNames, foundMetrics)
|
||||
|
||||
// Add widget to results for all found metrics
|
||||
@@ -286,14 +286,14 @@ func (module *module) checkBuilderQueriesForMetricNames(query map[string]interfa
|
||||
}
|
||||
}
|
||||
|
||||
// checkClickHouseQueriesForMetricNames checks clickhouse_sql[] array for metric names in query strings
|
||||
func (module *module) checkClickHouseQueriesForMetricNames(ctx context.Context, query map[string]interface{}, metricNames []string, foundMetrics map[string]bool) {
|
||||
clickhouseSQL, ok := query["clickhouse_sql"].([]interface{})
|
||||
// checkDatastoreQueriesForMetricNames checks datastore_sql[] array for metric names in query strings
|
||||
func (module *module) checkDatastoreQueriesForMetricNames(ctx context.Context, query map[string]interface{}, metricNames []string, foundMetrics map[string]bool) {
|
||||
datastoreSQL, ok := query["datastore_sql"].([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
for _, chQuery := range clickhouseSQL {
|
||||
for _, chQuery := range datastoreSQL {
|
||||
chQueryMap, ok := chQuery.(map[string]interface{})
|
||||
if !ok {
|
||||
continue
|
||||
@@ -305,10 +305,10 @@ func (module *module) checkClickHouseQueriesForMetricNames(ctx context.Context,
|
||||
}
|
||||
|
||||
// Parse query to extract metric names
|
||||
result, err := module.queryParser.AnalyzeQueryFilter(ctx, qbtypes.QueryTypeClickHouseSQL, queryStr)
|
||||
result, err := module.queryParser.AnalyzeQueryFilter(ctx, qbtypes.QueryTypeDatastoreSQL, queryStr)
|
||||
if err != nil {
|
||||
// Log warning and continue - parsing errors shouldn't break the search
|
||||
module.settings.Logger().WarnContext(ctx, "failed to parse ClickHouse query", "query", queryStr, "error", err)
|
||||
module.settings.Logger().WarnContext(ctx, "failed to parse Datastore query", "query", queryStr, "error", err)
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ type Config struct {
|
||||
}
|
||||
|
||||
type TelemetryStoreConfig struct {
|
||||
// Threads is the number of threads to use for ClickHouse queries
|
||||
// Threads is the number of threads to use for Datastore queries
|
||||
Threads int `mapstructure:"threads"`
|
||||
}
|
||||
|
||||
|
||||
@@ -35,9 +35,9 @@ func (m *Module) ExportRawData(ctx context.Context, orgID valuer.UUID, rangeRequ
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
// Set clickhouse max threads
|
||||
// Set datastore max threads
|
||||
ctx := ctxtypes.SetClickhouseMaxThreads(ctx, ClickhouseExportRawDataMaxThreads)
|
||||
// Set clickhouse timeout
|
||||
// Set datastore timeout
|
||||
contextWithTimeout, cancel := context.WithTimeout(ctx, ClickhouseExportRawDataTimeout)
|
||||
defer cancel()
|
||||
defer close(errChan)
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"strconv"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
"github.com/hanzoai/o11y/pkg/modules/services"
|
||||
"github.com/hanzoai/o11y/pkg/querier"
|
||||
@@ -40,10 +40,10 @@ func (m *module) FetchTopLevelOperations(ctx context.Context, start time.Time, s
|
||||
|
||||
db := m.TelemetryStore.ClickhouseDB()
|
||||
query := fmt.Sprintf("SELECT name, serviceName, max(time) as ts FROM %s.%s WHERE time >= @start", telemetrytraces.DBName, telemetrytraces.TopLevelOperationsTableName)
|
||||
args := []any{clickhouse.Named("start", start)}
|
||||
args := []any{datastore.Named("start", start)}
|
||||
if len(services) > 0 {
|
||||
query += " AND serviceName IN @services"
|
||||
args = append(args, clickhouse.Named("services", services))
|
||||
args = append(args, datastore.Named("services", services))
|
||||
}
|
||||
query += " GROUP BY name, serviceName ORDER BY ts DESC LIMIT 5000"
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ func sanitizeClause(clause string) string {
|
||||
return "AND " + clause
|
||||
}
|
||||
|
||||
func ValidateTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.ClickHouseQuery, error) {
|
||||
func ValidateTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.DatastoreQuery, error) {
|
||||
funnelSteps := funnel.Steps
|
||||
|
||||
// Build step data for the dynamic query builder
|
||||
@@ -58,12 +58,12 @@ func ValidateTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunn
|
||||
|
||||
query := BuildFunnelValidationQuery(steps, timeRange.StartTime, timeRange.EndTime)
|
||||
|
||||
return &v3.ClickHouseQuery{
|
||||
return &v3.DatastoreQuery{
|
||||
Query: query,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetFunnelAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.ClickHouseQuery, error) {
|
||||
func GetFunnelAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.DatastoreQuery, error) {
|
||||
funnelSteps := funnel.Steps
|
||||
|
||||
// Build step data for the dynamic query builder
|
||||
@@ -108,10 +108,10 @@ func GetFunnelAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange trace
|
||||
|
||||
query := BuildFunnelOverviewQuery(steps, timeRange.StartTime, timeRange.EndTime)
|
||||
|
||||
return &v3.ClickHouseQuery{Query: query}, nil
|
||||
return &v3.DatastoreQuery{Query: query}, nil
|
||||
}
|
||||
|
||||
func GetFunnelStepAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.ClickHouseQuery, error) {
|
||||
func GetFunnelStepAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.DatastoreQuery, error) {
|
||||
if stepStart == stepEnd {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "step start and end cannot be the same for /step/overview")
|
||||
}
|
||||
@@ -163,10 +163,10 @@ func GetFunnelStepAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange t
|
||||
|
||||
query := BuildFunnelStepOverviewQuery(steps, timeRange.StartTime, timeRange.EndTime, stepStart, stepEnd)
|
||||
|
||||
return &v3.ClickHouseQuery{Query: query}, nil
|
||||
return &v3.DatastoreQuery{Query: query}, nil
|
||||
}
|
||||
|
||||
func GetStepAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.ClickHouseQuery, error) {
|
||||
func GetStepAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange) (*v3.DatastoreQuery, error) {
|
||||
funnelSteps := funnel.Steps
|
||||
|
||||
// Build step data for the dynamic query builder
|
||||
@@ -203,12 +203,12 @@ func GetStepAnalytics(funnel *tracefunneltypes.StorableFunnel, timeRange tracefu
|
||||
|
||||
query := BuildFunnelCountQuery(steps, timeRange.StartTime, timeRange.EndTime)
|
||||
|
||||
return &v3.ClickHouseQuery{
|
||||
return &v3.DatastoreQuery{
|
||||
Query: query,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func GetSlowestTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.ClickHouseQuery, error) {
|
||||
func GetSlowestTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.DatastoreQuery, error) {
|
||||
funnelSteps := funnel.Steps
|
||||
containsErrorT1 := 0
|
||||
containsErrorT2 := 0
|
||||
@@ -264,11 +264,11 @@ func GetSlowestTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefu
|
||||
latencyPointerT1,
|
||||
latencyPointerT2,
|
||||
)
|
||||
return &v3.ClickHouseQuery{Query: query}, nil
|
||||
return &v3.DatastoreQuery{Query: query}, nil
|
||||
}
|
||||
|
||||
// TODO: Showing traces with error which are slow makes little sense as a product. We should show the error spans directly in the funnel chart. Rather showing traces which has drop between steps will be more relevant
|
||||
func GetErroredTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.ClickHouseQuery, error) {
|
||||
func GetErroredTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefunneltypes.TimeRange, stepStart, stepEnd int64) (*v3.DatastoreQuery, error) {
|
||||
funnelSteps := funnel.Steps
|
||||
containsErrorT1 := 0
|
||||
containsErrorT2 := 0
|
||||
@@ -324,5 +324,5 @@ func GetErroredTraces(funnel *tracefunneltypes.StorableFunnel, timeRange tracefu
|
||||
latencyPointerT1,
|
||||
latencyPointerT2,
|
||||
)
|
||||
return &v3.ClickHouseQuery{Query: query}, nil
|
||||
return &v3.DatastoreQuery{Query: query}, nil
|
||||
}
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package clickhouseprometheus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
"github.com/hanzoai/o11y/pkg/factory"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/constants"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
"github.com/hanzoai/o11y/pkg/types/ctxtypes"
|
||||
"github.com/hanzoai/o11y/pkg/types/instrumentationtypes"
|
||||
"github.com/hanzoai/o11y/pkg/types/telemetrytypes"
|
||||
promValue "github.com/prometheus/prometheus/model/value"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/storage/remote"
|
||||
)
|
||||
|
||||
type client struct {
|
||||
settings factory.ScopedProviderSettings
|
||||
telemetryStore telemetrystore.TelemetryStore
|
||||
}
|
||||
|
||||
func NewReadClient(settings factory.ScopedProviderSettings, telemetryStore telemetrystore.TelemetryStore) remote.ReadClient {
|
||||
return &client{
|
||||
settings: settings,
|
||||
telemetryStore: telemetryStore,
|
||||
}
|
||||
}
|
||||
|
||||
func (client *client) Read(ctx context.Context, query *prompb.Query, sortSeries bool) (storage.SeriesSet, error) {
|
||||
if len(query.Matchers) == 2 {
|
||||
var hasJob bool
|
||||
var queryString string
|
||||
for _, m := range query.Matchers {
|
||||
if m.Type == prompb.LabelMatcher_EQ && m.Name == "job" && m.Value == "rawsql" {
|
||||
hasJob = true
|
||||
}
|
||||
if m.Type == prompb.LabelMatcher_EQ && m.Name == "query" {
|
||||
queryString = m.Value
|
||||
}
|
||||
}
|
||||
|
||||
if hasJob && queryString != "" {
|
||||
res, err := client.queryRaw(ctx, queryString, int64(query.EndTimestampMs))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return remote.FromQueryResult(sortSeries, res), nil
|
||||
}
|
||||
}
|
||||
|
||||
var metricName string
|
||||
for _, matcher := range query.Matchers {
|
||||
if matcher.Name == "__name__" {
|
||||
metricName = matcher.Value
|
||||
}
|
||||
}
|
||||
|
||||
clickhouseQuery, args, err := client.queryToClickhouseQuery(ctx, query, metricName, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fingerprints, err := client.getFingerprintsFromClickhouseQuery(ctx, clickhouseQuery, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(fingerprints) == 0 {
|
||||
return remote.FromQueryResult(sortSeries, new(prompb.QueryResult)), nil
|
||||
}
|
||||
|
||||
clickhouseSubQuery, args, err := client.queryToClickhouseQuery(ctx, query, metricName, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
res := new(prompb.QueryResult)
|
||||
timeseries, err := client.querySamples(ctx, int64(query.StartTimestampMs), int64(query.EndTimestampMs), fingerprints, metricName, clickhouseSubQuery, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res.Timeseries = timeseries
|
||||
|
||||
return remote.FromQueryResult(sortSeries, res), nil
|
||||
}
|
||||
|
||||
func (c *client) ReadMultiple(ctx context.Context, queries []*prompb.Query, sortSeries bool) (storage.SeriesSet, error) {
|
||||
if len(queries) == 0 {
|
||||
return storage.EmptySeriesSet(), nil
|
||||
}
|
||||
if len(queries) == 1 {
|
||||
return c.Read(ctx, queries[0], sortSeries)
|
||||
}
|
||||
|
||||
type result struct {
|
||||
ss storage.SeriesSet
|
||||
err error
|
||||
}
|
||||
|
||||
results := make([]result, len(queries))
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(len(queries))
|
||||
for i, q := range queries {
|
||||
go func(i int, q *prompb.Query) {
|
||||
defer wg.Done()
|
||||
ss, err := c.Read(ctx, q, sortSeries)
|
||||
results[i] = result{ss, err}
|
||||
}(i, q)
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
sets := make([]storage.SeriesSet, 0, len(queries))
|
||||
for _, r := range results {
|
||||
if r.err != nil {
|
||||
return nil, r.err
|
||||
}
|
||||
sets = append(sets, r.ss)
|
||||
}
|
||||
return storage.NewMergeSeriesSet(sets, 0, storage.ChainedSeriesMerge), nil
|
||||
}
|
||||
|
||||
func (client *client) queryToClickhouseQuery(_ context.Context, query *prompb.Query, metricName string, subQuery bool) (string, []any, error) {
|
||||
var clickHouseQuery string
|
||||
var conditions []string
|
||||
var argCount int = 0
|
||||
var selectString string = "fingerprint, any(labels)"
|
||||
if subQuery {
|
||||
argCount = 1
|
||||
selectString = "fingerprint"
|
||||
}
|
||||
|
||||
start, end, tableName := getStartAndEndAndTableName(query.StartTimestampMs, query.EndTimestampMs)
|
||||
|
||||
var args []any
|
||||
conditions = append(conditions, fmt.Sprintf("metric_name = $%d", argCount+1))
|
||||
conditions = append(conditions, "temporality IN ['Cumulative', 'Unspecified']")
|
||||
conditions = append(conditions, fmt.Sprintf("unix_milli >= %d AND unix_milli < %d", start, end))
|
||||
|
||||
normalized := true
|
||||
if constants.IsDotMetricsEnabled {
|
||||
normalized = false
|
||||
}
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("__normalized = %v", normalized))
|
||||
|
||||
args = append(args, metricName)
|
||||
for _, m := range query.Matchers {
|
||||
switch m.Type {
|
||||
case prompb.LabelMatcher_EQ:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, $%d) = $%d", argCount+2, argCount+3))
|
||||
case prompb.LabelMatcher_NEQ:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, $%d) != $%d", argCount+2, argCount+3))
|
||||
case prompb.LabelMatcher_RE:
|
||||
conditions = append(conditions, fmt.Sprintf("match(JSONExtractString(labels, $%d), $%d)", argCount+2, argCount+3))
|
||||
case prompb.LabelMatcher_NRE:
|
||||
conditions = append(conditions, fmt.Sprintf("not match(JSONExtractString(labels, $%d), $%d)", argCount+2, argCount+3))
|
||||
default:
|
||||
return "", nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "unsupported or invalid matcher type: %s", m.Type.String())
|
||||
}
|
||||
args = append(args, m.Name, m.Value)
|
||||
argCount += 2
|
||||
}
|
||||
|
||||
whereClause := strings.Join(conditions, " AND ")
|
||||
|
||||
clickHouseQuery = fmt.Sprintf(`SELECT %s FROM %s.%s WHERE %s GROUP BY fingerprint`, selectString, databaseName, tableName, whereClause)
|
||||
|
||||
return clickHouseQuery, args, nil
|
||||
}
|
||||
|
||||
func (client *client) getFingerprintsFromClickhouseQuery(ctx context.Context, query string, args []any) (map[uint64][]prompb.Label, error) {
|
||||
ctx = client.withClickhousePrometheusContext(ctx, "getFingerprintsFromClickhouseQuery")
|
||||
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
fingerprints := make(map[uint64][]prompb.Label)
|
||||
|
||||
var fingerprint uint64
|
||||
var labelString string
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(&fingerprint, &labelString); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels, _, err := unmarshalLabels(labelString, fingerprint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fingerprints[fingerprint] = labels
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return fingerprints, nil
|
||||
}
|
||||
|
||||
func (client *client) querySamples(ctx context.Context, start int64, end int64, fingerprints map[uint64][]prompb.Label, metricName string, subQuery string, args []any) ([]*prompb.TimeSeries, error) {
|
||||
ctx = client.withClickhousePrometheusContext(ctx, "querySamples")
|
||||
argCount := len(args)
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT metric_name, fingerprint, unix_milli, value, flags
|
||||
FROM %s.%s
|
||||
WHERE metric_name = $1 AND fingerprint GLOBAL IN (%s) AND unix_milli >= $%s AND unix_milli <= $%s ORDER BY fingerprint, unix_milli;`,
|
||||
databaseName, distributedSamplesV4, subQuery, strconv.Itoa(argCount+2), strconv.Itoa(argCount+3))
|
||||
query = strings.TrimSpace(query)
|
||||
|
||||
allArgs := append([]any{metricName}, args...)
|
||||
allArgs = append(allArgs, start, end)
|
||||
|
||||
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query, allArgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var res []*prompb.TimeSeries
|
||||
var ts *prompb.TimeSeries
|
||||
var fingerprint, prevFingerprint uint64
|
||||
var timestampMs, prevTimestamp int64
|
||||
var value float64
|
||||
var flags uint32
|
||||
|
||||
prevTimestamp = math.MinInt64
|
||||
|
||||
for rows.Next() {
|
||||
if err := rows.Scan(&metricName, &fingerprint, ×tampMs, &value, &flags); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// collect samples in time series
|
||||
if fingerprint != prevFingerprint {
|
||||
// add collected time series to result
|
||||
prevFingerprint = fingerprint
|
||||
if ts != nil {
|
||||
res = append(res, ts)
|
||||
}
|
||||
|
||||
labels := fingerprints[fingerprint]
|
||||
ts = &prompb.TimeSeries{
|
||||
Labels: labels,
|
||||
}
|
||||
prevTimestamp = math.MinInt64
|
||||
}
|
||||
|
||||
if flags&1 == 1 {
|
||||
value = math.Float64frombits(promValue.StaleNaN)
|
||||
}
|
||||
|
||||
if timestampMs == prevTimestamp {
|
||||
continue
|
||||
}
|
||||
prevTimestamp = timestampMs
|
||||
|
||||
// add samples to current time series
|
||||
ts.Samples = append(ts.Samples, prompb.Sample{
|
||||
Timestamp: timestampMs,
|
||||
Value: value,
|
||||
})
|
||||
}
|
||||
|
||||
// add last time series
|
||||
if ts != nil {
|
||||
res = append(res, ts)
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func (client *client) queryRaw(ctx context.Context, query string, ts int64) (*prompb.QueryResult, error) {
|
||||
ctx = client.withClickhousePrometheusContext(ctx, "queryRaw")
|
||||
|
||||
rows, err := client.telemetryStore.ClickhouseDB().Query(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
columns := rows.Columns()
|
||||
var res prompb.QueryResult
|
||||
targets := make([]any, len(columns))
|
||||
for i := range targets {
|
||||
targets[i] = new(scanner)
|
||||
}
|
||||
|
||||
for rows.Next() {
|
||||
if err = rows.Scan(targets...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
labels := make([]prompb.Label, 0, len(columns))
|
||||
var value float64
|
||||
for i, c := range columns {
|
||||
v := targets[i].(*scanner)
|
||||
switch c {
|
||||
case "value":
|
||||
value = v.f
|
||||
default:
|
||||
labels = append(labels, prompb.Label{
|
||||
Name: c,
|
||||
Value: v.s,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
res.Timeseries = append(res.Timeseries, &prompb.TimeSeries{
|
||||
Labels: labels,
|
||||
Samples: []prompb.Sample{{
|
||||
Value: value,
|
||||
Timestamp: ts,
|
||||
}},
|
||||
})
|
||||
}
|
||||
if err = rows.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (client *client) withClickhousePrometheusContext(ctx context.Context, functionName string) context.Context {
|
||||
comments := map[string]string{
|
||||
instrumentationtypes.TelemetrySignal: telemetrytypes.SignalMetrics.StringValue(),
|
||||
instrumentationtypes.CodeNamespace: "clickhouse-prometheus",
|
||||
instrumentationtypes.CodeFunctionName: functionName,
|
||||
}
|
||||
return ctxtypes.NewContextWithCommentVals(ctx, comments)
|
||||
}
|
||||
@@ -1,211 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package clickhouseprometheus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore/telemetrystoretest"
|
||||
cmock "github.com/srikanthccv/ClickHouse-go-mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
"sort"
|
||||
"testing"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// Test for querySamples method
|
||||
func TestClient_QuerySamples(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
cols := make([]cmock.ColumnType, 0)
|
||||
cols = append(cols, cmock.ColumnType{Name: "metric_name", Type: "String"})
|
||||
cols = append(cols, cmock.ColumnType{Name: "fingerprint", Type: "UInt64"})
|
||||
cols = append(cols, cmock.ColumnType{Name: "unix_milli", Type: "Int64"})
|
||||
cols = append(cols, cmock.ColumnType{Name: "value", Type: "Float64"})
|
||||
cols = append(cols, cmock.ColumnType{Name: "flags", Type: "UInt32"})
|
||||
tests := []struct {
|
||||
name string
|
||||
start int64
|
||||
end int64
|
||||
fingerprints map[uint64][]prompb.Label
|
||||
metricName string
|
||||
subQuery string
|
||||
args []any
|
||||
setupMock func(mock cmock.ClickConnMockCommon, args ...any)
|
||||
expectedTimeSeries int
|
||||
expectError bool
|
||||
description string
|
||||
result []*prompb.TimeSeries
|
||||
}{
|
||||
{
|
||||
name: "successful samples retrieval",
|
||||
start: int64(1000),
|
||||
end: int64(2000),
|
||||
fingerprints: map[uint64][]prompb.Label{
|
||||
123: {
|
||||
{Name: "__name__", Value: "cpu_usage"},
|
||||
{Name: "instance", Value: "localhost:9090"},
|
||||
},
|
||||
456: {
|
||||
{Name: "__name__", Value: "cpu_usage"},
|
||||
{Name: "instance", Value: "localhost:9091"},
|
||||
},
|
||||
},
|
||||
metricName: "cpu_usage",
|
||||
subQuery: "SELECT metric_name, fingerprint, unix_milli, value, flags",
|
||||
expectedTimeSeries: 2,
|
||||
expectError: false,
|
||||
description: "Should successfully retrieve samples for multiple time series",
|
||||
setupMock: func(mock cmock.ClickConnMockCommon, args ...any) {
|
||||
values := [][]interface{}{
|
||||
{"cpu_usage", uint64(123), int64(1001), float64(1.1), uint32(0)},
|
||||
{"cpu_usage", uint64(123), int64(1001), float64(1.1), uint32(0)},
|
||||
{"cpu_usage", uint64(456), int64(1001), float64(1.2), uint32(0)},
|
||||
{"cpu_usage", uint64(456), int64(1001), float64(1.2), uint32(0)},
|
||||
{"cpu_usage", uint64(456), int64(1001), float64(1.2), uint32(0)},
|
||||
}
|
||||
mock.ExpectQuery("SELECT metric_name, fingerprint, unix_milli, value, flags").WithArgs(args...).WillReturnRows(
|
||||
cmock.NewRows(cols, values),
|
||||
)
|
||||
},
|
||||
result: []*prompb.TimeSeries{
|
||||
{
|
||||
Labels: []prompb.Label{
|
||||
{Name: "__name__", Value: "cpu_usage"},
|
||||
{Name: "instance", Value: "localhost:9090"},
|
||||
},
|
||||
Samples: []prompb.Sample{
|
||||
{Timestamp: 1001, Value: 1.1},
|
||||
},
|
||||
},
|
||||
{
|
||||
Labels: []prompb.Label{
|
||||
{Name: "__name__", Value: "cpu_usage"},
|
||||
{Name: "instance", Value: "localhost:9091"},
|
||||
},
|
||||
Samples: []prompb.Sample{
|
||||
{Timestamp: 1001, Value: 1.2},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
readClient := client{telemetryStore: telemetryStore}
|
||||
if tt.setupMock != nil {
|
||||
tt.setupMock(telemetryStore.Mock(), tt.metricName, tt.start, tt.end)
|
||||
|
||||
}
|
||||
result, err := readClient.querySamples(ctx, tt.start, tt.end, tt.fingerprints, tt.metricName, tt.subQuery, tt.args)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
assert.Nil(t, result)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedTimeSeries, len(result))
|
||||
assert.Equal(t, result, tt.result)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestClient_getFingerprintsFromClickhouseQuery(t *testing.T) {
|
||||
cols := []cmock.ColumnType{
|
||||
{Name: "fingerprint", Type: "UInt64"},
|
||||
{Name: "labels", Type: "String"},
|
||||
}
|
||||
|
||||
sortLabels := func(ls []prompb.Label) {
|
||||
sort.Slice(ls, func(i, j int) bool {
|
||||
if ls[i].Name == ls[j].Name {
|
||||
return ls[i].Value < ls[j].Value
|
||||
}
|
||||
return ls[i].Name < ls[j].Name
|
||||
})
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
start, end int64
|
||||
metricName string
|
||||
subQuery string
|
||||
args []any
|
||||
setupMock func(m cmock.ClickConnMockCommon, args ...any)
|
||||
want map[uint64][]prompb.Label
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "happy-path - two fingerprints",
|
||||
start: 1000,
|
||||
end: 2000,
|
||||
metricName: "cpu_usage",
|
||||
subQuery: `SELECT fingerprint,labels`,
|
||||
// args slice is empty here, but test‑case still owns it
|
||||
args: []any{},
|
||||
|
||||
setupMock: func(m cmock.ClickConnMockCommon, args ...any) {
|
||||
rows := [][]any{
|
||||
{uint64(123), `{"t1":"s1","t2":"s2"}`},
|
||||
{uint64(234), `{"t1":"s1","t2":"s2"}`},
|
||||
}
|
||||
m.ExpectQuery(`SELECT fingerprint,labels`).WithArgs(args...).WillReturnRows(
|
||||
cmock.NewRows(cols, rows),
|
||||
)
|
||||
},
|
||||
|
||||
want: map[uint64][]prompb.Label{
|
||||
123: {
|
||||
{Name: "fingerprint", Value: "123"},
|
||||
{Name: "t1", Value: "s1"},
|
||||
{Name: "t2", Value: "s2"},
|
||||
},
|
||||
234: {
|
||||
{Name: "fingerprint", Value: "234"},
|
||||
{Name: "t1", Value: "s1"},
|
||||
{Name: "t2", Value: "s2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
store := telemetrystoretest.New(
|
||||
telemetrystore.Config{Provider: "clickhouse"},
|
||||
sqlmock.QueryMatcherRegexp,
|
||||
)
|
||||
|
||||
if tc.setupMock != nil {
|
||||
tc.setupMock(store.Mock(), tc.args...)
|
||||
}
|
||||
|
||||
c := client{telemetryStore: store}
|
||||
|
||||
got, err := c.getFingerprintsFromClickhouseQuery(ctx, tc.subQuery, tc.args)
|
||||
if tc.wantErr {
|
||||
require.Error(t, err)
|
||||
require.Nil(t, got)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, len(tc.want), len(got), "fingerprint map length mismatch")
|
||||
for fp, expLabels := range tc.want {
|
||||
gotLabels, ok := got[fp]
|
||||
require.Truef(t, ok, "missing fingerprint %d", fp)
|
||||
|
||||
sortLabels(expLabels)
|
||||
sortLabels(gotLabels)
|
||||
|
||||
assert.Equalf(t, expLabels, gotLabels, "labels mismatch for fingerprint %d", fp)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package clickhouseprometheus
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"strconv"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/prometheus/prometheus/prompb"
|
||||
)
|
||||
|
||||
// Unmarshals JSON into Prometheus labels. It does not preserve order.
|
||||
func unmarshalLabels(s string, fingerprint uint64) ([]prompb.Label, string, error) {
|
||||
var metricName string
|
||||
m := make(map[string]string)
|
||||
if err := json.Unmarshal([]byte(s), &m); err != nil {
|
||||
return nil, metricName, err
|
||||
}
|
||||
res := make([]prompb.Label, 0, len(m))
|
||||
for n, v := range m {
|
||||
if n == "__name__" {
|
||||
metricName = v
|
||||
}
|
||||
|
||||
res = append(res, prompb.Label{
|
||||
Name: n,
|
||||
Value: v,
|
||||
})
|
||||
}
|
||||
res = append(res, prompb.Label{
|
||||
Name: prometheus.FingerprintAsPromLabelName,
|
||||
Value: strconv.FormatUint(fingerprint, 10),
|
||||
})
|
||||
return res, metricName, nil
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package clickhouseprometheus
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/factory"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/storage/remote"
|
||||
)
|
||||
|
||||
var stCallback = func() (int64, error) {
|
||||
return int64(model.Latest), nil
|
||||
}
|
||||
|
||||
type provider struct {
|
||||
settings factory.ScopedProviderSettings
|
||||
telemetryStore telemetrystore.TelemetryStore
|
||||
engine *prometheus.Engine
|
||||
queryable storage.SampleAndChunkQueryable
|
||||
}
|
||||
|
||||
func NewFactory(telemetryStore telemetrystore.TelemetryStore) factory.ProviderFactory[prometheus.Prometheus, prometheus.Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("clickhouse"), func(ctx context.Context, providerSettings factory.ProviderSettings, config prometheus.Config) (prometheus.Prometheus, error) {
|
||||
return New(ctx, providerSettings, config, telemetryStore)
|
||||
})
|
||||
}
|
||||
|
||||
func New(ctx context.Context, providerSettings factory.ProviderSettings, config prometheus.Config, telemetryStore telemetrystore.TelemetryStore) (prometheus.Prometheus, error) {
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/hanzoai/o11y/pkg/prometheus/clickhouseprometheus")
|
||||
|
||||
readClient := NewReadClient(settings, telemetryStore)
|
||||
|
||||
return &provider{
|
||||
settings: settings,
|
||||
telemetryStore: telemetryStore,
|
||||
engine: prometheus.NewEngine(settings.Logger(), config),
|
||||
queryable: remote.NewSampleAndChunkQueryableClient(readClient, labels.EmptyLabels(), []*labels.Matcher{}, false, stCallback),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) Engine() *prometheus.Engine {
|
||||
return provider.engine
|
||||
}
|
||||
|
||||
func (provider *provider) Storage() storage.Queryable {
|
||||
return provider
|
||||
}
|
||||
|
||||
func (provider *provider) Querier(mint, maxt int64) (storage.Querier, error) {
|
||||
querier, err := provider.queryable.Querier(mint, maxt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return storage.NewMergeQuerier(nil, []storage.Querier{querier}, storage.ChainedSeriesMerge), nil
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package clickhouseprometheus
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var _ sql.Scanner = (*scanner)(nil)
|
||||
|
||||
type scanner struct {
|
||||
f float64
|
||||
s string
|
||||
}
|
||||
|
||||
func (s *scanner) Scan(val any) error {
|
||||
s.f = 0
|
||||
s.s = ""
|
||||
|
||||
s.s = fmt.Sprintf("%v", val)
|
||||
switch val := val.(type) {
|
||||
case int64:
|
||||
s.f = float64(val)
|
||||
case uint64:
|
||||
s.f = float64(val)
|
||||
case float64:
|
||||
s.f = val
|
||||
case []byte:
|
||||
s.s = string(val)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package clickhouseprometheus
|
||||
|
||||
import "time"
|
||||
|
||||
const (
|
||||
databaseName string = "observe_metrics"
|
||||
distributedTimeSeriesV4 string = "distributed_time_series_v4"
|
||||
distributedTimeSeriesV46hrs string = "distributed_time_series_v4_6hrs"
|
||||
distributedTimeSeriesV41day string = "distributed_time_series_v4_1day"
|
||||
distributedSamplesV4 string = "distributed_samples_v4"
|
||||
)
|
||||
|
||||
var (
|
||||
sixHoursInMilliseconds = time.Hour.Milliseconds() * 6
|
||||
oneDayInMilliseconds = time.Hour.Milliseconds() * 24
|
||||
)
|
||||
|
||||
// Returns the start time, end time and the table name to use for the query.
|
||||
//
|
||||
// If time range is less than 6 hours, we need to use the `time_series_v4` table
|
||||
// else if time range is less than 1 day and greater than 6 hours, we need to use the `time_series_v4_6hrs` table
|
||||
// else we need to use the `time_series_v4_1day` table
|
||||
func getStartAndEndAndTableName(start, end int64) (int64, int64, string) {
|
||||
var tableName string
|
||||
|
||||
if end-start <= sixHoursInMilliseconds {
|
||||
// adjust the start time to nearest 1 hour
|
||||
start = start - (start % (time.Hour.Milliseconds() * 1))
|
||||
tableName = distributedTimeSeriesV4
|
||||
} else if end-start <= oneDayInMilliseconds {
|
||||
// adjust the start time to nearest 6 hours
|
||||
start = start - (start % (time.Hour.Milliseconds() * 6))
|
||||
tableName = distributedTimeSeriesV46hrs
|
||||
} else {
|
||||
// adjust the start time to nearest 1 day
|
||||
start = start - (start % (time.Hour.Milliseconds() * 24))
|
||||
tableName = distributedTimeSeriesV41day
|
||||
}
|
||||
|
||||
return start, end, tableName
|
||||
}
|
||||
@@ -41,5 +41,5 @@ func (c Config) Validate() error {
|
||||
}
|
||||
|
||||
func (c Config) Provider() string {
|
||||
return "clickhouse"
|
||||
return "datastore"
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
// Package nooprometheus is the default prometheus.Prometheus provider.
|
||||
//
|
||||
// o11y's canonical query path goes through ClickHouse SQL directly —
|
||||
// o11y's canonical query path goes through Datastore SQL directly —
|
||||
// PromQL support is signoz-inherited and not part of the default stack.
|
||||
// This noop returns a working (empty) Engine + Queryable so o11y boots
|
||||
// without the prometheus/prometheus/storage/remote chain (which pulls
|
||||
// google api → s2a-go → google.golang.org/grpc).
|
||||
//
|
||||
// Real PromQL support is gated behind -tags signoz via
|
||||
// pkg/prometheus/clickhouseprometheus.
|
||||
// pkg/prometheus/datastoreprometheus.
|
||||
package nooprometheus
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package prometheustest
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/factory"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus/clickhouseprometheus"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
"github.com/prometheus/common/model"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/storage"
|
||||
"github.com/prometheus/prometheus/storage/remote"
|
||||
)
|
||||
|
||||
var _ prometheus.Prometheus = (*Provider)(nil)
|
||||
|
||||
type Provider struct {
|
||||
queryable storage.SampleAndChunkQueryable
|
||||
engine *prometheus.Engine
|
||||
}
|
||||
|
||||
var stCallback = func() (int64, error) {
|
||||
return int64(model.Latest), nil
|
||||
}
|
||||
|
||||
func New(ctx context.Context, providerSettings factory.ProviderSettings, config prometheus.Config, telemetryStore telemetrystore.TelemetryStore) *Provider {
|
||||
|
||||
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/hanzoai/o11y/pkg/prometheus/prometheustest")
|
||||
|
||||
engine := prometheus.NewEngine(settings.Logger(), config)
|
||||
|
||||
readClient := clickhouseprometheus.NewReadClient(settings, telemetryStore)
|
||||
|
||||
queryable := remote.NewSampleAndChunkQueryableClient(readClient, labels.EmptyLabels(), []*labels.Matcher{}, false, stCallback)
|
||||
|
||||
return &Provider{
|
||||
engine: engine,
|
||||
queryable: queryable,
|
||||
}
|
||||
}
|
||||
|
||||
func (provider *Provider) Engine() *prometheus.Engine {
|
||||
return provider.engine
|
||||
}
|
||||
|
||||
func (provider *Provider) Storage() storage.Queryable {
|
||||
return provider.queryable
|
||||
}
|
||||
|
||||
func (provider *Provider) Close() error {
|
||||
if provider.engine != nil {
|
||||
provider.engine.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package prometheustest
|
||||
|
||||
import (
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/prometheus/prometheus/model/labels"
|
||||
"github.com/prometheus/prometheus/promql"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestRemoveExtraLabels(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
res *promql.Result
|
||||
remove []string
|
||||
wantErr bool
|
||||
verify func(t *testing.T, result *promql.Result, removed []string)
|
||||
}{
|
||||
{
|
||||
name: "vector – label removed",
|
||||
res: &promql.Result{
|
||||
Value: promql.Vector{
|
||||
promql.Sample{
|
||||
Metric: labels.FromStrings(
|
||||
"__name__", "http_requests_total",
|
||||
"job", "demo",
|
||||
"drop_me", "dropped",
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
remove: []string{"drop_me"},
|
||||
verify: func(t *testing.T, result *promql.Result, removed []string) {
|
||||
k := result.Value.(promql.Vector)
|
||||
for _, str := range removed {
|
||||
get := k[0].Metric.Get(str)
|
||||
if get != "" {
|
||||
t.Fatalf("label not removed")
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "scalar – nothing to strip",
|
||||
res: &promql.Result{
|
||||
Value: promql.Scalar{V: 99, T: 1},
|
||||
},
|
||||
remove: []string{"irrelevant"},
|
||||
verify: func(t *testing.T, result *promql.Result, removed []string) {
|
||||
sc := result.Value.(promql.Scalar)
|
||||
if sc.V != 99 || sc.T != 1 {
|
||||
t.Fatalf("scalar unexpectedly modified: got %+v", sc)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "matrix – label removed",
|
||||
res: &promql.Result{
|
||||
Value: promql.Matrix{
|
||||
promql.Series{
|
||||
Metric: labels.FromStrings(
|
||||
"__name__", "http_requests_total",
|
||||
"pod", "p0",
|
||||
"drop_me", "dropped",
|
||||
),
|
||||
Floats: []promql.FPoint{{T: 0, F: 1}, {T: 1, F: 2}},
|
||||
},
|
||||
promql.Series{
|
||||
Metric: labels.FromStrings(
|
||||
"__name__", "http_requests_total",
|
||||
"pod", "p0",
|
||||
"drop_me", "dropped",
|
||||
),
|
||||
Floats: []promql.FPoint{{T: 0, F: 1}, {T: 1, F: 2}},
|
||||
},
|
||||
},
|
||||
},
|
||||
remove: []string{"drop_me"},
|
||||
verify: func(t *testing.T, result *promql.Result, removed []string) {
|
||||
mat := result.Value.(promql.Matrix)
|
||||
for _, str := range removed {
|
||||
for _, k := range mat {
|
||||
if k.Metric.Get(str) != "" {
|
||||
t.Fatalf("label not removed")
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := prometheus.RemoveExtraLabels(tc.res, tc.remove...)
|
||||
if tc.wantErr && err == nil {
|
||||
t.Fatalf("expected error, got nil")
|
||||
}
|
||||
if !tc.wantErr && err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if tc.verify != nil {
|
||||
tc.verify(t, tc.res, tc.remove)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrylogs"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
@@ -223,7 +223,7 @@ func (q *builderQuery[T]) executeWithContext(ctx context.Context, query string,
|
||||
totalBytes := uint64(0)
|
||||
elapsed := time.Duration(0)
|
||||
|
||||
ctx = clickhouse.Context(ctx, clickhouse.WithProgress(func(p *clickhouse.Progress) {
|
||||
ctx = datastore.Context(ctx, datastore.WithProgress(func(p *datastore.Progress) {
|
||||
totalRows += p.Rows
|
||||
totalBytes += p.Bytes
|
||||
elapsed += p.Elapsed
|
||||
@@ -333,7 +333,7 @@ func (q *builderQuery[T]) executeWindowList(ctx context.Context) (*qbtypes.Resul
|
||||
reqLimit = 10_000 // sane upper-bound default
|
||||
}
|
||||
offsetLeft := q.spec.Offset
|
||||
need := reqLimit + offsetLeft // rows to fetch from ClickHouse
|
||||
need := reqLimit + offsetLeft // rows to fetch from Datastore
|
||||
|
||||
var rows []*qbtypes.RawRow
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
"github.com/hanzoai/o11y/pkg/querybuilder"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
@@ -23,7 +23,7 @@ type chSQLQuery struct {
|
||||
logger *slog.Logger
|
||||
telemetryStore telemetrystore.TelemetryStore
|
||||
|
||||
query qbtypes.ClickHouseQuery
|
||||
query qbtypes.DatastoreQuery
|
||||
args []any
|
||||
fromMS uint64
|
||||
toMS uint64
|
||||
@@ -36,7 +36,7 @@ var _ qbtypes.Query = (*chSQLQuery)(nil)
|
||||
func newchSQLQuery(
|
||||
logger *slog.Logger,
|
||||
telemetryStore telemetrystore.TelemetryStore,
|
||||
query qbtypes.ClickHouseQuery,
|
||||
query qbtypes.DatastoreQuery,
|
||||
args []any,
|
||||
tr qbtypes.TimeRange,
|
||||
kind qbtypes.RequestType,
|
||||
@@ -84,7 +84,7 @@ func (q *chSQLQuery) renderVars(query string, vars map[string]qbtypes.VariableIt
|
||||
query = strings.Replace(query, fmt.Sprintf("$%s", k), fmt.Sprint(varsData[k]), -1)
|
||||
}
|
||||
|
||||
tmpl := template.New("clickhouse-query")
|
||||
tmpl := template.New("datastore-query")
|
||||
tmpl, err := tmpl.Parse(query)
|
||||
if err != nil {
|
||||
return "", errors.WrapInternalf(err, errors.CodeInternal, "error while replacing template variables")
|
||||
@@ -108,7 +108,7 @@ func (q *chSQLQuery) Execute(ctx context.Context) (*qbtypes.Result, error) {
|
||||
totalBytes := uint64(0)
|
||||
elapsed := time.Duration(0)
|
||||
|
||||
ctx = clickhouse.Context(ctx, clickhouse.WithProgress(func(p *clickhouse.Progress) {
|
||||
ctx = datastore.Context(ctx, datastore.WithProgress(func(p *datastore.Progress) {
|
||||
totalRows += p.Rows
|
||||
totalBytes += p.Bytes
|
||||
elapsed += p.Elapsed
|
||||
@@ -125,7 +125,7 @@ func (q *chSQLQuery) Execute(ctx context.Context) (*qbtypes.Result, error) {
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// TODO: map the errors from ClickHouse to our error types
|
||||
// TODO: map the errors from Datastore to our error types
|
||||
payload, err := consume(rows, q.kind, nil, qbtypes.Step{}, q.query.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
var (
|
||||
aggRe = regexp.MustCompile(`^__result_(\d+)$`)
|
||||
// legacyReservedColumnTargetAliases identifies result value from a user
|
||||
// written clickhouse query. The column alias indcate which value is
|
||||
// written datastore query. The column alias indcate which value is
|
||||
// to be considered as final result (or target)
|
||||
legacyReservedColumnTargetAliases = []string{"__result", "__value", "result", "res", "value"}
|
||||
)
|
||||
|
||||
@@ -10,7 +10,7 @@ func toStrArrayString(strs ...string) string {
|
||||
return fmt.Sprintf("[%s]", strings.Join(strs, ","))
|
||||
}
|
||||
|
||||
// formatValue formats the value to be used in clickhouse query
|
||||
// formatValue formats the value to be used in datastore query
|
||||
func formatValueForCH(v any) string {
|
||||
// if it's pointer convert it to a value
|
||||
v = getPointerValue(v)
|
||||
@@ -143,7 +143,7 @@ func getPointerValue(v any) any {
|
||||
}
|
||||
|
||||
func quoteEscapedString(str string) string {
|
||||
// https://clickhouse.com/docs/en/sql-reference/syntax#string
|
||||
// https://datastore.com/docs/en/sql-reference/syntax#string
|
||||
str = strings.ReplaceAll(str, `\`, `\\`)
|
||||
str = strings.ReplaceAll(str, `'`, `\'`)
|
||||
return str
|
||||
|
||||
@@ -35,7 +35,7 @@ func getqueryInfo(spec any) queryInfo {
|
||||
return queryInfo{Name: s.Name, Disabled: s.Disabled}
|
||||
case qbtypes.PromQuery:
|
||||
return queryInfo{Name: s.Name, Disabled: s.Disabled, Step: s.Step}
|
||||
case qbtypes.ClickHouseQuery:
|
||||
case qbtypes.DatastoreQuery:
|
||||
return queryInfo{Name: s.Name, Disabled: s.Disabled}
|
||||
}
|
||||
return queryInfo{}
|
||||
@@ -91,7 +91,7 @@ func (q *querier) postProcessResults(ctx context.Context, results map[string]any
|
||||
|
||||
// merge result only needed for non-CH query
|
||||
if len(req.CompositeQuery.Queries) == 1 {
|
||||
if req.CompositeQuery.Queries[0].Type == qbtypes.QueryTypeClickHouseSQL {
|
||||
if req.CompositeQuery.Queries[0].Type == qbtypes.QueryTypeDatastoreSQL {
|
||||
retResult := map[string]any{}
|
||||
for name, v := range typedResults {
|
||||
retResult[name] = v.Value
|
||||
|
||||
@@ -247,9 +247,9 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
|
||||
}
|
||||
req.CompositeQuery.Queries[idx].Spec = spec
|
||||
}
|
||||
} else if query.Type == qbtypes.QueryTypeClickHouseSQL {
|
||||
} else if query.Type == qbtypes.QueryTypeDatastoreSQL {
|
||||
switch spec := query.Spec.(type) {
|
||||
case qbtypes.ClickHouseQuery:
|
||||
case qbtypes.DatastoreQuery:
|
||||
if strings.TrimSpace(spec.Query) != "" {
|
||||
event.MetricsUsed = strings.Contains(spec.Query, "observe_metrics")
|
||||
event.LogsUsed = strings.Contains(spec.Query, "observe_logs")
|
||||
@@ -308,8 +308,8 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
|
||||
if spec, ok := query.Spec.(qbtypes.PromQuery); ok {
|
||||
queryName = spec.Name
|
||||
}
|
||||
case qbtypes.QueryTypeClickHouseSQL:
|
||||
if spec, ok := query.Spec.(qbtypes.ClickHouseQuery); ok {
|
||||
case qbtypes.QueryTypeDatastoreSQL:
|
||||
if spec, ok := query.Spec.(qbtypes.DatastoreQuery); ok {
|
||||
queryName = spec.Name
|
||||
}
|
||||
case qbtypes.QueryTypeBuilder:
|
||||
@@ -336,10 +336,10 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, req *qbtype
|
||||
promqlQuery := newPromqlQuery(q.logger, q.promEngine, promQuery, qbtypes.TimeRange{From: req.Start, To: req.End}, req.RequestType, tmplVars)
|
||||
queries[promQuery.Name] = promqlQuery
|
||||
steps[promQuery.Name] = promQuery.Step
|
||||
case qbtypes.QueryTypeClickHouseSQL:
|
||||
chQuery, ok := query.Spec.(qbtypes.ClickHouseQuery)
|
||||
case qbtypes.QueryTypeDatastoreSQL:
|
||||
chQuery, ok := query.Spec.(qbtypes.DatastoreQuery)
|
||||
if !ok {
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid clickhouse query spec %T", query.Spec)
|
||||
return nil, errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid datastore query spec %T", query.Spec)
|
||||
}
|
||||
chSQLQuery := newchSQLQuery(q.logger, q.telemetryStore, chQuery, nil, qbtypes.TimeRange{From: req.Start, To: req.End}, req.RequestType, tmplVars)
|
||||
queries[chQuery.Name] = chSQLQuery
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
"github.com/hanzoai/o11y/pkg/types/ctxtypes"
|
||||
"github.com/hanzoai/o11y/pkg/types/instrumentationtypes"
|
||||
@@ -64,7 +64,7 @@ func (q *traceOperatorQuery) executeWithContext(ctx context.Context, query strin
|
||||
totalBytes := uint64(0)
|
||||
elapsed := time.Duration(0)
|
||||
|
||||
ctx = clickhouse.Context(ctx, clickhouse.WithProgress(func(p *clickhouse.Progress) {
|
||||
ctx = datastore.Context(ctx, datastore.WithProgress(func(p *datastore.Progress) {
|
||||
totalRows += p.Rows
|
||||
totalBytes += p.Bytes
|
||||
elapsed += p.Elapsed
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
// Clickhouse reader methods for powering QB filter suggestions
|
||||
package clickhouseReader
|
||||
package datastoreReader
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
func (r *ClickHouseReader) GetQBFilterSuggestionsForLogs(
|
||||
func (r *DatastoreReader) GetQBFilterSuggestionsForLogs(
|
||||
ctx context.Context,
|
||||
req *v3.QBFilterSuggestionsRequest,
|
||||
) (*v3.QBFilterSuggestionsResponse, *model.ApiError) {
|
||||
@@ -127,7 +127,7 @@ func (r *ClickHouseReader) GetQBFilterSuggestionsForLogs(
|
||||
|
||||
// Get up to `limit` values seen for each attribute in `attributes`
|
||||
// Returns a slice of slices where the ith slice has values for ith entry in `attributes`
|
||||
func (r *ClickHouseReader) getValuesForLogAttributes(
|
||||
func (r *DatastoreReader) getValuesForLogAttributes(
|
||||
ctx context.Context, attributes []v3.AttributeKey, limit uint64,
|
||||
) ([][]any, *model.ApiError) {
|
||||
/*
|
||||
+3
-3
@@ -1,9 +1,9 @@
|
||||
package clickhouseReader
|
||||
package datastoreReader
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
)
|
||||
|
||||
type Encoding string
|
||||
@@ -96,7 +96,7 @@ type namespaceConfig struct {
|
||||
}
|
||||
|
||||
// Connecto defines how to connect to the database
|
||||
type Connector func(cfg *namespaceConfig) (clickhouse.Conn, error)
|
||||
type Connector func(cfg *namespaceConfig) (datastore.Conn, error)
|
||||
|
||||
// Options store storage plugin related configs
|
||||
type Options struct {
|
||||
+5
-5
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/model"
|
||||
"github.com/google/uuid"
|
||||
"go.uber.org/zap"
|
||||
@@ -38,7 +38,7 @@ func (tracker *inMemoryQueryProgressTracker) ReportQueryStarted(
|
||||
}
|
||||
|
||||
func (tracker *inMemoryQueryProgressTracker) ReportQueryProgress(
|
||||
queryId string, chProgress *clickhouse.Progress,
|
||||
queryId string, chProgress *datastore.Progress,
|
||||
) *model.ApiError {
|
||||
queryTracker, err := tracker.getQueryTracker(queryId)
|
||||
if err != nil {
|
||||
@@ -109,13 +109,13 @@ func newQueryTracker(queryId string) *queryTracker {
|
||||
}
|
||||
}
|
||||
|
||||
func (qt *queryTracker) handleProgressUpdate(p *clickhouse.Progress) {
|
||||
func (qt *queryTracker) handleProgressUpdate(p *datastore.Progress) {
|
||||
qt.lock.Lock()
|
||||
defer qt.lock.Unlock()
|
||||
|
||||
if qt.isFinished {
|
||||
zap.L().Warn(
|
||||
"received clickhouse progress update for finished query",
|
||||
"received datastore progress update for finished query",
|
||||
zap.String("queryId", qt.queryId), zap.Any("progress", p),
|
||||
)
|
||||
return
|
||||
@@ -247,7 +247,7 @@ func (ch *queryProgressSubscription) close() {
|
||||
}
|
||||
}
|
||||
|
||||
func updateQueryProgress(qp *model.QueryProgress, chProgress *clickhouse.Progress) {
|
||||
func updateQueryProgress(qp *model.QueryProgress, chProgress *datastore.Progress) {
|
||||
qp.ReadRows += chProgress.Rows
|
||||
qp.ReadBytes += chProgress.Bytes
|
||||
qp.ElapsedMs += uint64(chProgress.Elapsed.Milliseconds())
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
package queryprogress
|
||||
|
||||
import (
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/model"
|
||||
)
|
||||
|
||||
@@ -11,8 +11,8 @@ type QueryProgressTracker interface {
|
||||
// Returns a cleanup function that must be called after the query finishes.
|
||||
ReportQueryStarted(queryId string) (postQueryCleanup func(), apiErr *model.ApiError)
|
||||
|
||||
// Report progress stats received from clickhouse for `queryId`
|
||||
ReportQueryProgress(queryId string, chProgress *clickhouse.Progress) *model.ApiError
|
||||
// Report progress stats received from datastore for `queryId`
|
||||
ReportQueryProgress(queryId string, chProgress *datastore.Progress) *model.ApiError
|
||||
|
||||
// Subscribe to progress updates for `queryId`
|
||||
// The returned channel will produce `QueryProgress` instances representing
|
||||
+4
-4
@@ -4,7 +4,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/model"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -16,7 +16,7 @@ func TestQueryProgressTracking(t *testing.T) {
|
||||
|
||||
testQueryId := "test-query"
|
||||
|
||||
testProgress := &clickhouse.Progress{}
|
||||
testProgress := &datastore.Progress{}
|
||||
err := tracker.ReportQueryProgress(testQueryId, testProgress)
|
||||
require.NotNil(err, "shouldn't be able to report query progress before query has been started")
|
||||
require.Equal(err.Type(), model.ErrorNotFound)
|
||||
@@ -30,7 +30,7 @@ func TestQueryProgressTracking(t *testing.T) {
|
||||
reportQueryFinished, err := tracker.ReportQueryStarted(testQueryId)
|
||||
require.Nil(err, "should be able to report start of a query to be tracked")
|
||||
|
||||
testProgress1 := &clickhouse.Progress{
|
||||
testProgress1 := &datastore.Progress{
|
||||
Rows: 10,
|
||||
Bytes: 20,
|
||||
TotalRows: 100,
|
||||
@@ -59,7 +59,7 @@ func TestQueryProgressTracking(t *testing.T) {
|
||||
default:
|
||||
}
|
||||
|
||||
testProgress2 := &clickhouse.Progress{
|
||||
testProgress2 := &datastore.Progress{
|
||||
Rows: 20,
|
||||
Bytes: 40,
|
||||
TotalRows: 100,
|
||||
+323
-323
File diff suppressed because it is too large
Load Diff
+1
-1
@@ -1,4 +1,4 @@
|
||||
package clickhouseReader
|
||||
package datastoreReader
|
||||
|
||||
import (
|
||||
"testing"
|
||||
@@ -151,7 +151,7 @@ type APIHandler struct {
|
||||
}
|
||||
|
||||
type APIHandlerOpts struct {
|
||||
// business data reader e.g. clickhouse
|
||||
// business data reader e.g. datastore
|
||||
Reader interfaces.Reader
|
||||
|
||||
// rule manager handles rule crud operations
|
||||
@@ -2017,7 +2017,7 @@ func (aH *APIHandler) getFeatureFlags(w http.ResponseWriter, r *http.Request) {
|
||||
func (aH *APIHandler) getHealth(w http.ResponseWriter, r *http.Request) {
|
||||
_, ok := r.URL.Query()["live"]
|
||||
if ok {
|
||||
err := aH.reader.CheckClickHouse(r.Context())
|
||||
err := aH.reader.CheckDatastore(r.Context())
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorStatusServiceUnavailable}, nil)
|
||||
return
|
||||
@@ -2157,7 +2157,7 @@ func (aH *APIHandler) onboardProducers(
|
||||
return
|
||||
}
|
||||
|
||||
chq, err := kafka.BuildClickHouseQuery(messagingQueue, kafka.KafkaQueue, "onboard_producers")
|
||||
chq, err := kafka.BuildDatastoreQuery(messagingQueue, kafka.KafkaQueue, "onboard_producers")
|
||||
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
@@ -2259,7 +2259,7 @@ func (aH *APIHandler) onboardConsumers(
|
||||
return
|
||||
}
|
||||
|
||||
chq, err := kafka.BuildClickHouseQuery(messagingQueue, kafka.KafkaQueue, "onboard_consumers")
|
||||
chq, err := kafka.BuildDatastoreQuery(messagingQueue, kafka.KafkaQueue, "onboard_consumers")
|
||||
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
@@ -2656,7 +2656,7 @@ func (aH *APIHandler) getProducerData(w http.ResponseWriter, r *http.Request) {
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
return
|
||||
}
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
|
||||
resp := v3.QueryRangeResponse{
|
||||
Result: result,
|
||||
@@ -2713,7 +2713,7 @@ func (aH *APIHandler) getConsumerData(w http.ResponseWriter, r *http.Request) {
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
return
|
||||
}
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
|
||||
resp := v3.QueryRangeResponse{
|
||||
Result: result,
|
||||
@@ -2771,7 +2771,7 @@ func (aH *APIHandler) getPartitionOverviewLatencyData(w http.ResponseWriter, r *
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
return
|
||||
}
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
|
||||
resp := v3.QueryRangeResponse{
|
||||
Result: result,
|
||||
@@ -2829,7 +2829,7 @@ func (aH *APIHandler) getConsumerPartitionLatencyData(w http.ResponseWriter, r *
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
return
|
||||
}
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
|
||||
resp := v3.QueryRangeResponse{
|
||||
Result: result,
|
||||
@@ -2946,7 +2946,7 @@ func (aH *APIHandler) getProducerThroughputOverview(w http.ResponseWriter, r *ht
|
||||
|
||||
resultFetchLatency = postprocess.TransformToTableForBuilderQueries(latencyColumnResult, queryRangeParams)
|
||||
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
|
||||
result = append(result, resultFetchLatency[0])
|
||||
resp := v3.QueryRangeResponse{
|
||||
@@ -3005,7 +3005,7 @@ func (aH *APIHandler) getProducerThroughputDetails(w http.ResponseWriter, r *htt
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
return
|
||||
}
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
|
||||
resp := v3.QueryRangeResponse{
|
||||
Result: result,
|
||||
@@ -3063,7 +3063,7 @@ func (aH *APIHandler) getConsumerThroughputOverview(w http.ResponseWriter, r *ht
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
return
|
||||
}
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
|
||||
resp := v3.QueryRangeResponse{
|
||||
Result: result,
|
||||
@@ -3121,7 +3121,7 @@ func (aH *APIHandler) getConsumerThroughputDetails(w http.ResponseWriter, r *htt
|
||||
RespondError(w, apiErrObj, errQueriesByName)
|
||||
return
|
||||
}
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
|
||||
resp := v3.QueryRangeResponse{
|
||||
Result: result,
|
||||
@@ -4536,7 +4536,7 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que
|
||||
"queryId", queryIdHeader, "error", apiErr)
|
||||
|
||||
} else {
|
||||
// Adding queryId to the context signals clickhouse queries to report progress
|
||||
// Adding queryId to the context signals datastore queries to report progress
|
||||
//lint:ignore SA1029 ignore for now
|
||||
ctx = context.WithValue(ctx, "queryId", queryIdHeader)
|
||||
|
||||
@@ -4567,7 +4567,7 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que
|
||||
|
||||
aH.sendQueryResultEvents(r, result, queryRangeParams, "v3")
|
||||
// only adding applyFunctions instead of postProcess since experssion are
|
||||
// are executed in clickhouse directly and we wanted to add support for timeshift
|
||||
// are executed in datastore directly and we wanted to add support for timeshift
|
||||
if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder {
|
||||
postprocess.ApplyFunctions(result, queryRangeParams)
|
||||
}
|
||||
@@ -4577,8 +4577,8 @@ func (aH *APIHandler) queryRangeV3(ctx context.Context, queryRangeParams *v3.Que
|
||||
}
|
||||
|
||||
if queryRangeParams.CompositeQuery.PanelType == v3.PanelTypeTable && queryRangeParams.FormatForWeb {
|
||||
if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeDatastoreSQL {
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
} else if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder {
|
||||
result = postprocess.TransformToTableForBuilderQueries(result, queryRangeParams)
|
||||
}
|
||||
@@ -4889,9 +4889,9 @@ func (aH *APIHandler) queryRangeV4(ctx context.Context, queryRangeParams *v3.Que
|
||||
|
||||
if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeBuilder {
|
||||
result, err = postprocess.PostProcessResult(result, queryRangeParams)
|
||||
} else if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL &&
|
||||
} else if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeDatastoreSQL &&
|
||||
queryRangeParams.CompositeQuery.PanelType == v3.PanelTypeTable && queryRangeParams.FormatForWeb {
|
||||
result = postprocess.TransformToTableForClickHouseQueries(result)
|
||||
result = postprocess.TransformToTableForDatastoreQueries(result)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@@ -4989,7 +4989,7 @@ func (aH *APIHandler) getQueueOverview(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err != nil {
|
||||
slog.Error(err.Error())
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -5194,13 +5194,13 @@ func (aH *APIHandler) handleValidateTraces(w http.ResponseWriter, r *http.Reques
|
||||
|
||||
chq, err := traceFunnelsModule.ValidateTraces(funnel, timeRange)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5231,13 +5231,13 @@ func (aH *APIHandler) handleFunnelAnalytics(w http.ResponseWriter, r *http.Reque
|
||||
|
||||
chq, err := traceFunnelsModule.GetFunnelAnalytics(funnel, stepTransition.TimeRange)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5268,13 +5268,13 @@ func (aH *APIHandler) handleFunnelStepAnalytics(w http.ResponseWriter, r *http.R
|
||||
|
||||
chq, err := traceFunnelsModule.GetFunnelStepAnalytics(funnel, stepTransition.TimeRange, stepTransition.StepStart, stepTransition.StepEnd)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5305,13 +5305,13 @@ func (aH *APIHandler) handleStepAnalytics(w http.ResponseWriter, r *http.Request
|
||||
|
||||
chq, err := traceFunnelsModule.GetStepAnalytics(funnel, timeRange)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5342,13 +5342,13 @@ func (aH *APIHandler) handleFunnelSlowTraces(w http.ResponseWriter, r *http.Requ
|
||||
|
||||
chq, err := traceFunnelsModule.GetSlowestTraces(funnel, req.TimeRange, req.StepStart, req.StepEnd)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5379,13 +5379,13 @@ func (aH *APIHandler) handleFunnelErrorTraces(w http.ResponseWriter, r *http.Req
|
||||
|
||||
chq, err := traceFunnelsModule.GetErroredTraces(funnel, req.TimeRange, req.StepStart, req.StepEnd)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5413,13 +5413,13 @@ func (aH *APIHandler) handleValidateTracesWithPayload(w http.ResponseWriter, r *
|
||||
EndTime: req.EndTime,
|
||||
})
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5441,13 +5441,13 @@ func (aH *APIHandler) handleFunnelAnalyticsWithPayload(w http.ResponseWriter, r
|
||||
EndTime: req.EndTime,
|
||||
})
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5469,13 +5469,13 @@ func (aH *APIHandler) handleStepAnalyticsWithPayload(w http.ResponseWriter, r *h
|
||||
EndTime: req.EndTime,
|
||||
})
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5497,13 +5497,13 @@ func (aH *APIHandler) handleFunnelStepAnalyticsWithPayload(w http.ResponseWriter
|
||||
EndTime: req.EndTime,
|
||||
}, req.StepStart, req.StepEnd)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5525,13 +5525,13 @@ func (aH *APIHandler) handleFunnelSlowTracesWithPayload(w http.ResponseWriter, r
|
||||
EndTime: req.EndTime,
|
||||
}, req.StepStart, req.StepEnd)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
@@ -5553,13 +5553,13 @@ func (aH *APIHandler) handleFunnelErrorTracesWithPayload(w http.ResponseWriter,
|
||||
EndTime: req.EndTime,
|
||||
}, req.StepStart, req.StepEnd)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building clickhouse query: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error building datastore query: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
results, err := aH.reader.GetListResultV3(r.Context(), chq.Query)
|
||||
if err != nil {
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting clickhouse results to list: %v", err)}, nil)
|
||||
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: fmt.Errorf("error converting datastore results to list: %v", err)}, nil)
|
||||
return
|
||||
}
|
||||
aH.Respond(w, results)
|
||||
|
||||
@@ -268,7 +268,7 @@ func readFileIfUri(fs embed.FS, maybeFileUri string, basedir string) (interface{
|
||||
return nil, fmt.Errorf("unsupported file type %s", maybeFileUri)
|
||||
}
|
||||
|
||||
// copied from o11y clickhouse exporter's `sanitize` which
|
||||
// copied from o11y datastore exporter's `sanitize` which
|
||||
// in turn is copied from prometheus-go-metric-exporter
|
||||
//
|
||||
// replaces non-alphanumeric characters with underscores in s.
|
||||
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
@@ -19,7 +19,7 @@ func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string,
|
||||
// ToDo: propagate this through APIs when there are different handlers
|
||||
queueType := KafkaQueue
|
||||
|
||||
chq, err := BuildClickHouseQuery(messagingQueue, queueType, queryContext)
|
||||
chq, err := BuildDatastoreQuery(messagingQueue, queueType, queryContext)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -42,7 +42,7 @@ func BuildQueryRangeParams(messagingQueue *MessagingQueue, queryContext string,
|
||||
return queryRangeParams, nil
|
||||
}
|
||||
|
||||
func buildClickHouseQueryNetwork(messagingQueue *MessagingQueue, queueType string) (*v3.ClickHouseQuery, error) {
|
||||
func buildDatastoreQueryNetwork(messagingQueue *MessagingQueue, queueType string) (*v3.DatastoreQuery, error) {
|
||||
start := messagingQueue.Start
|
||||
end := messagingQueue.End
|
||||
|
||||
@@ -57,7 +57,7 @@ func buildClickHouseQueryNetwork(messagingQueue *MessagingQueue, queueType strin
|
||||
}
|
||||
|
||||
query := generateNetworkLatencyThroughputSQL(start, end, consumerGroup, partitionID, queueType)
|
||||
return &v3.ClickHouseQuery{
|
||||
return &v3.DatastoreQuery{
|
||||
Query: query,
|
||||
}, nil
|
||||
}
|
||||
@@ -286,7 +286,7 @@ func BuildQRParamsWithCache(
|
||||
var err error
|
||||
|
||||
if queryContext == "throughput" {
|
||||
chq, err := buildClickHouseQueryNetwork(messagingQueue, queueType)
|
||||
chq, err := buildDatastoreQueryNetwork(messagingQueue, queueType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -307,7 +307,7 @@ func BuildQRParamsWithCache(
|
||||
start := messagingQueue.Start
|
||||
end := messagingQueue.End
|
||||
query := generateProducerPartitionThroughputSQL(start, end, queueType)
|
||||
cq, err = buildCompositeQuery(&v3.ClickHouseQuery{
|
||||
cq, err = buildCompositeQuery(&v3.DatastoreQuery{
|
||||
Query: query,
|
||||
}, queryContext)
|
||||
|
||||
@@ -336,11 +336,11 @@ func BuildQRParamsWithCache(
|
||||
return queryRangeParams, err
|
||||
}
|
||||
|
||||
func BuildClickHouseQuery(
|
||||
func BuildDatastoreQuery(
|
||||
messagingQueue *MessagingQueue,
|
||||
queueType string,
|
||||
queryContext string,
|
||||
) (*v3.ClickHouseQuery, error) {
|
||||
) (*v3.DatastoreQuery, error) {
|
||||
|
||||
start := messagingQueue.Start
|
||||
end := messagingQueue.End
|
||||
@@ -406,24 +406,24 @@ func BuildClickHouseQuery(
|
||||
query = onboardConsumerSQL(start, end, queueType)
|
||||
}
|
||||
|
||||
return &v3.ClickHouseQuery{
|
||||
return &v3.DatastoreQuery{
|
||||
Query: query,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func buildCompositeQuery(chq *v3.ClickHouseQuery, queryContext string) (*v3.CompositeQuery, error) {
|
||||
func buildCompositeQuery(chq *v3.DatastoreQuery, queryContext string) (*v3.CompositeQuery, error) {
|
||||
|
||||
if queryContext == "producer-consumer-eval" {
|
||||
return &v3.CompositeQuery{
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
ClickHouseQueries: map[string]*v3.ClickHouseQuery{queryContext: chq},
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
DatastoreQueries: map[string]*v3.DatastoreQuery{queryContext: chq},
|
||||
PanelType: v3.PanelTypeList,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &v3.CompositeQuery{
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
ClickHouseQueries: map[string]*v3.ClickHouseQuery{queryContext: chq},
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
DatastoreQueries: map[string]*v3.DatastoreQuery{queryContext: chq},
|
||||
PanelType: v3.PanelTypeTable,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
|
||||
)
|
||||
|
||||
func BuildOverviewQuery(queueList *QueueListRequest) (*v3.ClickHouseQuery, error) {
|
||||
func BuildOverviewQuery(queueList *QueueListRequest) (*v3.DatastoreQuery, error) {
|
||||
|
||||
err := queueList.Validate()
|
||||
if err != nil {
|
||||
@@ -13,7 +13,7 @@ func BuildOverviewQuery(queueList *QueueListRequest) (*v3.ClickHouseQuery, error
|
||||
|
||||
query := generateOverviewSQL(queueList.Start, queueList.End, queueList.Filters.Items)
|
||||
|
||||
return &v3.ClickHouseQuery{
|
||||
return &v3.DatastoreQuery{
|
||||
Query: query,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import (
|
||||
format "github.com/hanzoai/o11y/pkg/query-service/utils"
|
||||
)
|
||||
|
||||
// generateOverviewSQL builds the ClickHouse SQL query with optional filters.
|
||||
// generateOverviewSQL builds the Datastore SQL query with optional filters.
|
||||
// If a filter slice is empty, the query does not constrain on that field.
|
||||
func generateOverviewSQL(start, end int64, item []v3.FilterItem) string {
|
||||
// Convert from nanoseconds to float seconds in Go to avoid decimal overflow in ClickHouse
|
||||
// Convert from nanoseconds to float seconds in Go to avoid decimal overflow in Datastore
|
||||
startSeconds := float64(start) / 1e9
|
||||
endSeconds := float64(end) / 1e9
|
||||
|
||||
@@ -28,15 +28,15 @@ func generateOverviewSQL(start, end int64, item []v3.FilterItem) string {
|
||||
for _, filter := range item {
|
||||
switch filter.Key.Key {
|
||||
case "service.name":
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "service_name", format.ClickHouseFormattedValue(filter.Value)))
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "service_name", format.DatastoreFormattedValue(filter.Value)))
|
||||
case "name":
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "span_name", format.ClickHouseFormattedValue(filter.Value)))
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "span_name", format.DatastoreFormattedValue(filter.Value)))
|
||||
case "destination":
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "destination", format.ClickHouseFormattedValue(filter.Value)))
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "destination", format.DatastoreFormattedValue(filter.Value)))
|
||||
case "queue":
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "messaging_system", format.ClickHouseFormattedValue(filter.Value)))
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "messaging_system", format.DatastoreFormattedValue(filter.Value)))
|
||||
case "kind_string":
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "kind_string", format.ClickHouseFormattedValue(filter.Value)))
|
||||
whereClauses = append(whereClauses, fmt.Sprintf("%s IN (%s)", "kind_string", format.DatastoreFormattedValue(filter.Value)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,232 +1,27 @@
|
||||
//go:build signoz
|
||||
|
||||
// Log pipeline preview runs each candidate log through the configured
|
||||
// signoz-otel-collector pipeline to show how it'd be transformed. The
|
||||
// collector simulator pulls google.golang.org/grpc via the OTel
|
||||
// collector framework, so this whole feature is opt-in via -tags signoz.
|
||||
// Log pipeline preview is a signoz-inherited feature backed by the
|
||||
// signoz-otel-collector simulator, which pulls google.golang.org/grpc
|
||||
// via the OTel collector framework. Default builds ship a noop that
|
||||
// returns the input logs unchanged + a warning that preview requires
|
||||
// -tags signoz.
|
||||
//
|
||||
// Default builds use the noop in preview_noop.go.
|
||||
// O11yLogsToPLogs and PLogsToO11yLogs (plain pdata conversions, no grpc
|
||||
// pull) stay in preview.go and are excluded from this noop build.
|
||||
|
||||
package logparsingpipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz-otel-collector/pkg/collectorsimulator"
|
||||
_ "github.com/SigNoz/signoz-otel-collector/pkg/parser/grok"
|
||||
"github.com/SigNoz/signoz-otel-collector/processor/signozlogspipelineprocessor"
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/model"
|
||||
"github.com/hanzoai/o11y/pkg/types/pipelinetypes"
|
||||
"go.opentelemetry.io/collector/otelcol"
|
||||
"go.opentelemetry.io/collector/pdata/pcommon"
|
||||
"go.opentelemetry.io/collector/pdata/plog"
|
||||
)
|
||||
|
||||
func SimulatePipelinesProcessing(ctx context.Context, pipelines []pipelinetypes.GettablePipeline, logs []model.O11yLog) (
|
||||
[]model.O11yLog, []string, error) {
|
||||
if len(pipelines) < 1 {
|
||||
return logs, nil, nil
|
||||
}
|
||||
|
||||
// Collector simulation does not guarantee that logs will come
|
||||
// out in the same order as in the input.
|
||||
//
|
||||
// Add a temp attribute for sorting logs in simulation output
|
||||
inputOrderAttribute := "__o11y_input_idx__"
|
||||
for i := 0; i < len(logs); i++ {
|
||||
if logs[i].Attributes_int64 == nil {
|
||||
logs[i].Attributes_int64 = map[string]int64{}
|
||||
}
|
||||
logs[i].Attributes_int64[inputOrderAttribute] = int64(i)
|
||||
}
|
||||
simulatorInputPLogs := O11yLogsToPLogs(logs)
|
||||
|
||||
processorFactories, err := otelcol.MakeFactoryMap(signozlogspipelineprocessor.NewFactory())
|
||||
if err != nil {
|
||||
return nil, nil, errors.WrapInternalf(err, CodeProcessorFactoryMapFailed, "could not construct processor factory map")
|
||||
}
|
||||
|
||||
// Pipelines translate to logtransformprocessors in otel collector config.
|
||||
// Each logtransformprocessor (stanza) does its own batching with a flush
|
||||
// interval of 100ms. So e2e processing time for logs grows linearly with
|
||||
// the number of logtransformprocessors involved.
|
||||
// See defaultFlushInterval at https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/pkg/stanza/adapter/emitter.go
|
||||
// TODO(Raj): Remove this after flushInterval is exposed in logtransformprocessor config
|
||||
timeout := time.Millisecond * time.Duration(len(pipelines)*100+100)
|
||||
|
||||
configGenerator := func(baseConf []byte) ([]byte, error) {
|
||||
updatedConf, err := GenerateCollectorConfigWithPipelines(baseConf, pipelines)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return updatedConf, nil
|
||||
}
|
||||
|
||||
outputPLogs, collectorErrs, simulationErr := collectorsimulator.SimulateLogsProcessing(
|
||||
ctx,
|
||||
processorFactories,
|
||||
configGenerator,
|
||||
simulatorInputPLogs,
|
||||
timeout,
|
||||
)
|
||||
if simulationErr != nil {
|
||||
if errors.Is(simulationErr, collectorsimulator.ErrInvalidConfig) {
|
||||
return nil, nil, errors.WrapInvalidInputf(simulationErr, errors.CodeInvalidInput, "invalid config")
|
||||
}
|
||||
return nil, nil, errors.WrapInternalf(simulationErr, errors.CodeInternal, "could not simulate log pipelines processing")
|
||||
}
|
||||
|
||||
outputO11yLogs := PLogsToO11yLogs(outputPLogs)
|
||||
|
||||
// Sort output logs by their order in the input and remove the temp ordering attribute
|
||||
sort.Slice(outputO11yLogs, func(i, j int) bool {
|
||||
iIdx := outputO11yLogs[i].Attributes_int64[inputOrderAttribute]
|
||||
jIdx := outputO11yLogs[j].Attributes_int64[inputOrderAttribute]
|
||||
return iIdx < jIdx
|
||||
})
|
||||
for _, sigLog := range outputO11yLogs {
|
||||
delete(sigLog.Attributes_int64, inputOrderAttribute)
|
||||
}
|
||||
|
||||
collectorWarnAndErrorLogs := []string{}
|
||||
for _, log := range collectorErrs {
|
||||
// if log is empty or log comes from featuregate.go, then remove it
|
||||
if log == "" || strings.Contains(log, "featuregate.go") {
|
||||
continue
|
||||
}
|
||||
collectorWarnAndErrorLogs = append(collectorWarnAndErrorLogs, log)
|
||||
}
|
||||
|
||||
return outputO11yLogs, collectorWarnAndErrorLogs, nil
|
||||
}
|
||||
|
||||
// plog doesn't contain an ID field.
|
||||
// O11yLog.ID is stored as a log attribute in plogs for processing
|
||||
// and gets hydrated back later.
|
||||
const O11yLogIdAttr = "__o11y_log_id__"
|
||||
|
||||
func O11yLogsToPLogs(logs []model.O11yLog) []plog.Logs {
|
||||
result := []plog.Logs{}
|
||||
|
||||
for _, log := range logs {
|
||||
pl := plog.NewLogs()
|
||||
rl := pl.ResourceLogs().AppendEmpty()
|
||||
|
||||
resourceAttribs := rl.Resource().Attributes()
|
||||
for k, v := range log.Resources_string {
|
||||
resourceAttribs.PutStr(k, v)
|
||||
}
|
||||
|
||||
scopeLog := rl.ScopeLogs().AppendEmpty()
|
||||
slRecord := scopeLog.LogRecords().AppendEmpty()
|
||||
|
||||
slRecord.SetTimestamp(pcommon.NewTimestampFromTime(
|
||||
time.Unix(0, int64(log.Timestamp)),
|
||||
))
|
||||
|
||||
var traceIdBuf [16]byte
|
||||
copy(traceIdBuf[:], []byte(log.TraceID))
|
||||
slRecord.SetTraceID(traceIdBuf)
|
||||
|
||||
var spanIdBuf [8]byte
|
||||
copy(spanIdBuf[:], []byte(log.SpanID))
|
||||
slRecord.SetSpanID(spanIdBuf)
|
||||
|
||||
slRecord.SetFlags(plog.LogRecordFlags(log.TraceFlags))
|
||||
|
||||
slRecord.SetSeverityText(log.SeverityText)
|
||||
slRecord.SetSeverityNumber(plog.SeverityNumber(log.SeverityNumber))
|
||||
|
||||
slRecord.Body().FromRaw(log.Body)
|
||||
|
||||
slAttribs := slRecord.Attributes()
|
||||
for k, v := range log.Attributes_int64 {
|
||||
slAttribs.PutInt(k, v)
|
||||
}
|
||||
for k, v := range log.Attributes_float64 {
|
||||
slAttribs.PutDouble(k, v)
|
||||
}
|
||||
for k, v := range log.Attributes_string {
|
||||
slAttribs.PutStr(k, v)
|
||||
}
|
||||
slAttribs.PutStr(O11yLogIdAttr, log.ID)
|
||||
|
||||
result = append(result, pl)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func PLogsToO11yLogs(plogs []plog.Logs) []model.O11yLog {
|
||||
result := []model.O11yLog{}
|
||||
|
||||
for _, pl := range plogs {
|
||||
|
||||
resourceLogsSlice := pl.ResourceLogs()
|
||||
for i := 0; i < resourceLogsSlice.Len(); i++ {
|
||||
rl := resourceLogsSlice.At(i)
|
||||
|
||||
scopeLogsSlice := rl.ScopeLogs()
|
||||
for j := 0; j < scopeLogsSlice.Len(); j++ {
|
||||
sl := scopeLogsSlice.At(j)
|
||||
|
||||
lrSlice := sl.LogRecords()
|
||||
for k := 0; k < lrSlice.Len(); k++ {
|
||||
lr := lrSlice.At(k)
|
||||
|
||||
// Recover ID for the log and remove temp attrib used for storing it
|
||||
o11yLogId := ""
|
||||
logIdVal, exists := lr.Attributes().Get(O11yLogIdAttr)
|
||||
if exists {
|
||||
o11yLogId = logIdVal.Str()
|
||||
}
|
||||
lr.Attributes().Remove(O11yLogIdAttr)
|
||||
|
||||
o11yLog := model.O11yLog{
|
||||
Timestamp: uint64(lr.Timestamp()),
|
||||
ID: o11yLogId,
|
||||
TraceID: lr.TraceID().String(),
|
||||
SpanID: lr.SpanID().String(),
|
||||
TraceFlags: uint32(lr.Flags()),
|
||||
SeverityText: lr.SeverityText(),
|
||||
SeverityNumber: uint8(lr.SeverityNumber()),
|
||||
Body: lr.Body().AsString(),
|
||||
Resources_string: pMapToStrMap(rl.Resource().Attributes()),
|
||||
Attributes_string: map[string]string{},
|
||||
Attributes_int64: map[string]int64{},
|
||||
Attributes_float64: map[string]float64{},
|
||||
}
|
||||
|
||||
// Populate o11yLog.Attributes_...
|
||||
lr.Attributes().Range(func(k string, v pcommon.Value) bool {
|
||||
if v.Type() == pcommon.ValueTypeDouble {
|
||||
o11yLog.Attributes_float64[k] = v.Double()
|
||||
} else if v.Type() == pcommon.ValueTypeInt {
|
||||
o11yLog.Attributes_int64[k] = v.Int()
|
||||
} else {
|
||||
o11yLog.Attributes_string[k] = v.AsString()
|
||||
}
|
||||
return true
|
||||
})
|
||||
|
||||
result = append(result, o11yLog)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func pMapToStrMap(pMap pcommon.Map) map[string]string {
|
||||
result := map[string]string{}
|
||||
pMap.Range(func(k string, v pcommon.Value) bool {
|
||||
result[k] = v.AsString()
|
||||
return true
|
||||
})
|
||||
return result
|
||||
// SimulatePipelinesProcessing is a no-op in default builds. Returns the
|
||||
// input logs untouched, plus a single warning string explaining why no
|
||||
// transformation happened. Build with -tags signoz to enable the real
|
||||
// collector-based simulator.
|
||||
func SimulatePipelinesProcessing(_ context.Context, _ []pipelinetypes.GettablePipeline, logs []model.O11yLog) ([]model.O11yLog, []string, error) {
|
||||
return logs, []string{
|
||||
"log pipeline preview disabled (signoz-otel-collector pulls google.golang.org/grpc — rebuild with -tags signoz to enable)",
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
//go:build !signoz
|
||||
|
||||
// Log pipeline preview is a signoz-inherited feature backed by the
|
||||
// signoz-otel-collector simulator, which pulls google.golang.org/grpc
|
||||
// via the OTel collector framework. Default builds ship a noop that
|
||||
// returns the input logs unchanged + a warning that preview requires
|
||||
// -tags signoz.
|
||||
//
|
||||
// O11yLogsToPLogs and PLogsToO11yLogs (plain pdata conversions, no grpc
|
||||
// pull) stay in preview.go and are excluded from this noop build.
|
||||
|
||||
package logparsingpipeline
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/query-service/model"
|
||||
"github.com/hanzoai/o11y/pkg/types/pipelinetypes"
|
||||
)
|
||||
|
||||
// SimulatePipelinesProcessing is a no-op in default builds. Returns the
|
||||
// input logs untouched, plus a single warning string explaining why no
|
||||
// transformation happened. Build with -tags signoz to enable the real
|
||||
// collector-based simulator.
|
||||
func SimulatePipelinesProcessing(_ context.Context, _ []pipelinetypes.GettablePipeline, logs []model.O11yLog) ([]model.O11yLog, []string, error) {
|
||||
return logs, []string{
|
||||
"log pipeline preview disabled (signoz-otel-collector pulls google.golang.org/grpc — rebuild with -tags signoz to enable)",
|
||||
}, nil
|
||||
}
|
||||
@@ -166,13 +166,13 @@ func GetJSONFilter(item v3.FilterItem) (string, error) {
|
||||
case v3.FilterOperatorExists, v3.FilterOperatorNotExists:
|
||||
filter = fmt.Sprintf(logsOp, key, GetPath(strings.Split(item.Key.Key, ".")[1:]))
|
||||
case v3.FilterOperatorRegex, v3.FilterOperatorNotRegex, v3.FilterOperatorHas, v3.FilterOperatorNotHas:
|
||||
fmtVal := utils.ClickHouseFormattedValue(value)
|
||||
fmtVal := utils.DatastoreFormattedValue(value)
|
||||
filter = fmt.Sprintf(logsOp, key, fmtVal)
|
||||
case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
|
||||
val := utils.QuoteEscapedString(fmt.Sprintf("%v", item.Value))
|
||||
filter = fmt.Sprintf("%s %s '%%%s%%'", key, logsOp, val)
|
||||
default:
|
||||
fmtVal := utils.ClickHouseFormattedValue(value)
|
||||
fmtVal := utils.DatastoreFormattedValue(value)
|
||||
filter = fmt.Sprintf("%s %s %s", key, logsOp, fmtVal)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -77,9 +77,9 @@ func getClickhouseLogsColumnDataType(columnDataType v3.AttributeKeyDataType) str
|
||||
return "string"
|
||||
}
|
||||
|
||||
// getClickhouseColumnName returns the corresponding clickhouse column name for the given attribute/resource key
|
||||
// getClickhouseColumnName returns the corresponding datastore column name for the given attribute/resource key
|
||||
func getClickhouseColumnName(key v3.AttributeKey) string {
|
||||
clickhouseColumn := key.Key
|
||||
datastoreColumn := key.Key
|
||||
if key.Key == constants.TIMESTAMP || key.Key == "id" {
|
||||
return key.Key
|
||||
}
|
||||
@@ -89,19 +89,19 @@ func getClickhouseColumnName(key v3.AttributeKey) string {
|
||||
if !key.IsColumn {
|
||||
columnType := GetClickhouseLogsColumnType(key.Type)
|
||||
columnDataType := getClickhouseLogsColumnDataType(key.DataType)
|
||||
clickhouseColumn = fmt.Sprintf("%s_%s_value[indexOf(%s_%s_key, '%s')]", columnType, columnDataType, columnType, columnDataType, key.Key)
|
||||
return clickhouseColumn
|
||||
datastoreColumn = fmt.Sprintf("%s_%s_value[indexOf(%s_%s_key, '%s')]", columnType, columnDataType, columnType, columnDataType, key.Key)
|
||||
return datastoreColumn
|
||||
}
|
||||
|
||||
// check if it is a static field
|
||||
if key.Type == v3.AttributeKeyTypeUnspecified {
|
||||
// name is the column name
|
||||
return clickhouseColumn
|
||||
return datastoreColumn
|
||||
}
|
||||
|
||||
// materialized column created from query
|
||||
clickhouseColumn = utils.GetClickhouseColumnName(string(key.Type), string(key.DataType), key.Key)
|
||||
return clickhouseColumn
|
||||
datastoreColumn = utils.GetClickhouseColumnName(string(key.Type), string(key.DataType), key.Key)
|
||||
return datastoreColumn
|
||||
}
|
||||
|
||||
// getSelectLabels returns the select labels for the query based on groupBy and aggregateOperator
|
||||
@@ -194,7 +194,7 @@ func buildLogsTimeSeriesFilterQuery(fs *v3.FilterSet, groupBy []v3.AttributeKey,
|
||||
conditions = append(conditions, GetExistsNexistsFilter(op, item))
|
||||
case v3.FilterOperatorRegex, v3.FilterOperatorNotRegex:
|
||||
columnName := getClickhouseColumnName(item.Key)
|
||||
fmtVal := utils.ClickHouseFormattedValue(value)
|
||||
fmtVal := utils.DatastoreFormattedValue(value)
|
||||
conditions = append(conditions, fmt.Sprintf(logsOp, columnName, fmtVal))
|
||||
case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
|
||||
columnName := getClickhouseColumnName(item.Key)
|
||||
@@ -207,7 +207,7 @@ func buildLogsTimeSeriesFilterQuery(fs *v3.FilterSet, groupBy []v3.AttributeKey,
|
||||
}
|
||||
default:
|
||||
columnName := getClickhouseColumnName(item.Key)
|
||||
fmtVal := utils.ClickHouseFormattedValue(value)
|
||||
fmtVal := utils.DatastoreFormattedValue(value)
|
||||
|
||||
// for use lower for like and ilike
|
||||
if op == v3.FilterOperatorLike || op == v3.FilterOperatorNotLike {
|
||||
@@ -445,7 +445,7 @@ func Having(items []v3.Having) string {
|
||||
// aggregate something and filter on that aggregate
|
||||
var having []string
|
||||
for _, item := range items {
|
||||
having = append(having, fmt.Sprintf("value %s %s", item.Operator, utils.ClickHouseFormattedValue(item.Value)))
|
||||
having = append(having, fmt.Sprintf("value %s %s", item.Operator, utils.DatastoreFormattedValue(item.Value)))
|
||||
}
|
||||
return strings.Join(having, " AND ")
|
||||
}
|
||||
|
||||
@@ -65,13 +65,13 @@ func GetJSONFilter(item v3.FilterItem) (string, error) {
|
||||
case v3.FilterOperatorExists, v3.FilterOperatorNotExists:
|
||||
filter = fmt.Sprintf(logsOp, key, logsV3.GetPath(strings.Split(item.Key.Key, ".")[1:]))
|
||||
case v3.FilterOperatorRegex, v3.FilterOperatorNotRegex, v3.FilterOperatorHas, v3.FilterOperatorNotHas:
|
||||
fmtVal := utils.ClickHouseFormattedValue(value)
|
||||
fmtVal := utils.DatastoreFormattedValue(value)
|
||||
filter = fmt.Sprintf(logsOp, key, fmtVal)
|
||||
case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
|
||||
val := utils.QuoteEscapedString(fmt.Sprintf("%v", item.Value))
|
||||
filter = fmt.Sprintf("%s %s '%%%s%%'", key, logsOp, val)
|
||||
default:
|
||||
fmtVal := utils.ClickHouseFormattedValue(value)
|
||||
fmtVal := utils.DatastoreFormattedValue(value)
|
||||
filter = fmt.Sprintf("%s %s %s", key, logsOp, fmtVal)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -144,12 +144,12 @@ func buildAttributeFilter(item v3.FilterItem) (string, error) {
|
||||
if (op != v3.FilterOperatorEqual && op != v3.FilterOperatorContains) || item.Key.DataType != v3.AttributeKeyDataTypeString {
|
||||
return "", fmt.Errorf("only = operator and string data type is supported for __attrs")
|
||||
}
|
||||
val := utils.ClickHouseFormattedValue(item.Value)
|
||||
val := utils.DatastoreFormattedValue(item.Value)
|
||||
return fmt.Sprintf("has(mapValues(attributes_string), %s)", val), nil
|
||||
}
|
||||
|
||||
keyName := getClickhouseKey(item.Key)
|
||||
fmtVal := utils.ClickHouseFormattedValue(value)
|
||||
fmtVal := utils.DatastoreFormattedValue(value)
|
||||
|
||||
if logsOp, ok := logOperators[op]; ok {
|
||||
switch op {
|
||||
@@ -221,7 +221,7 @@ func buildLogsTimeSeriesFilterQuery(fs *v3.FilterSet, groupBy []v3.AttributeKey,
|
||||
op := v3.FilterOperator(strings.ToLower(string(item.Operator)))
|
||||
|
||||
// add extra condition for map contains
|
||||
// by default clickhouse is not able to utilize indexes for keys with all operators.
|
||||
// by default datastore is not able to utilize indexes for keys with all operators.
|
||||
// mapContains forces the use of index.
|
||||
// for mat column it's is not required as it will already use the dedicated index.
|
||||
// skip the exists filter for operators such as !=, not like, not contains, not regex, not in
|
||||
|
||||
@@ -82,7 +82,7 @@ func AddMetricValueFilter(mq *v3.BuilderQuery) *v3.MetricValueFilter {
|
||||
return metricValueFilter
|
||||
}
|
||||
|
||||
// FormattedValue formats the value to be used in clickhouse query
|
||||
// FormattedValue formats the value to be used in datastore query
|
||||
func FormattedValue(v interface{}) string {
|
||||
switch x := v.(type) {
|
||||
case int:
|
||||
|
||||
@@ -42,7 +42,7 @@ func buildMetricQueryForTable(start, end, _ int64, mq *v3.BuilderQuery) (string,
|
||||
return "", err
|
||||
}
|
||||
|
||||
samplesTableTimeFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli <= %d", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
samplesTableTimeFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli <= %d", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
|
||||
// Select the aggregate value for interval
|
||||
queryTmplCounterInner :=
|
||||
|
||||
@@ -36,7 +36,7 @@ func buildDeltaMetricQuery(start, end, step int64, mq *v3.BuilderQuery) (string,
|
||||
return "", err
|
||||
}
|
||||
|
||||
samplesTableTimeFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli <= %d", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
samplesTableTimeFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli <= %d", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
|
||||
// Select the aggregate value for interval
|
||||
queryTmpl :=
|
||||
|
||||
@@ -49,7 +49,7 @@ func buildDeltaMetricQueryForTable(start, end, _ int64, mq *v3.BuilderQuery) (st
|
||||
return "", err
|
||||
}
|
||||
|
||||
samplesTableTimeFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli <= %d", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
samplesTableTimeFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli <= %d", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
|
||||
queryTmpl :=
|
||||
"SELECT %s toStartOfHour(now()) as ts," + // now() has no menaing & used as a placeholder for ts
|
||||
|
||||
@@ -60,7 +60,7 @@ func buildMetricQuery(start, end, step int64, mq *v3.BuilderQuery) (string, erro
|
||||
return "", err
|
||||
}
|
||||
|
||||
samplesTableTimeFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli < %d", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
samplesTableTimeFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli < %d", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
|
||||
// Select the aggregate value for interval
|
||||
queryTmpl :=
|
||||
@@ -296,7 +296,7 @@ func orderByAttributeKeyTags(items []v3.OrderBy, tags []v3.AttributeKey) string
|
||||
func having(items []v3.Having) string {
|
||||
var having []string
|
||||
for _, item := range items {
|
||||
having = append(having, fmt.Sprintf("%s %s %v", "value", item.Operator, utils.ClickHouseFormattedValue(item.Value)))
|
||||
having = append(having, fmt.Sprintf("%s %s %v", "value", item.Operator, utils.DatastoreFormattedValue(item.Value)))
|
||||
}
|
||||
return strings.Join(having, " AND ")
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/hanzoai/o11y/pkg/query-service/utils"
|
||||
)
|
||||
|
||||
// See https://clickhouse.com/docs/en/sql-reference/window-functions for more details on `lagInFrame` function
|
||||
// See https://datastore.com/docs/en/sql-reference/window-functions for more details on `lagInFrame` function
|
||||
//
|
||||
// Calculating the rate of change of a metric is a common use case.
|
||||
// Requests and errors are two examples of metrics that are often expressed as a rate of change.
|
||||
@@ -116,7 +116,7 @@ func prepareTimeAggregationSubQuery(start, end, step int64, mq *v3.BuilderQuery)
|
||||
return "", err
|
||||
}
|
||||
|
||||
samplesTableFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli < %d", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
samplesTableFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli < %d", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
|
||||
tableName := helpers.WhichSamplesTableToUse(start, end, mq)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ func prepareTimeAggregationSubQuery(start, end, step int64, mq *v3.BuilderQuery)
|
||||
return "", err
|
||||
}
|
||||
|
||||
samplesTableFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli < %d", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
samplesTableFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli < %d", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
tableName := helpers.WhichSamplesTableToUse(start, end, mq)
|
||||
|
||||
samplesTableFilter = helpers.AddFlagsFilters(samplesTableFilter, tableName)
|
||||
@@ -84,7 +84,7 @@ func prepareQueryOptimized(start, end, step int64, mq *v3.BuilderQuery) (string,
|
||||
return "", err
|
||||
}
|
||||
|
||||
samplesTableFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli < %d", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
samplesTableFilter := fmt.Sprintf("metric_name IN %s AND unix_milli >= %d AND unix_milli < %d", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key), start, end)
|
||||
|
||||
tableName := helpers.WhichSamplesTableToUse(start, end, mq)
|
||||
|
||||
|
||||
@@ -256,7 +256,7 @@ func PrepareTimeseriesFilterQuery(start, end int64, mq *v3.BuilderQuery) (string
|
||||
var fs *v3.FilterSet = mq.Filters
|
||||
var groupTags []v3.AttributeKey = mq.GroupBy
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("metric_name IN %s", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key)))
|
||||
conditions = append(conditions, fmt.Sprintf("metric_name IN %s", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key)))
|
||||
conditions = append(conditions, fmt.Sprintf("temporality = '%s'", mq.Temporality))
|
||||
if constants.IsDotMetricsEnabled {
|
||||
conditions = append(conditions, "__normalized = false")
|
||||
@@ -281,7 +281,7 @@ func PrepareTimeseriesFilterQuery(start, end int64, mq *v3.BuilderQuery) (string
|
||||
}
|
||||
var fmtVal string
|
||||
if op != v3.FilterOperatorExists && op != v3.FilterOperatorNotExists {
|
||||
fmtVal = utils.ClickHouseFormattedValue(toFormat)
|
||||
fmtVal = utils.DatastoreFormattedValue(toFormat)
|
||||
}
|
||||
switch op {
|
||||
case v3.FilterOperatorEqual:
|
||||
@@ -352,7 +352,7 @@ func PrepareTimeseriesFilterQueryV3(start, end int64, mq *v3.BuilderQuery) (stri
|
||||
var fs *v3.FilterSet = mq.Filters
|
||||
var groupTags []v3.AttributeKey = mq.GroupBy
|
||||
|
||||
conditions = append(conditions, fmt.Sprintf("metric_name IN %s", utils.ClickHouseFormattedMetricNames(mq.AggregateAttribute.Key)))
|
||||
conditions = append(conditions, fmt.Sprintf("metric_name IN %s", utils.DatastoreFormattedMetricNames(mq.AggregateAttribute.Key)))
|
||||
conditions = append(conditions, fmt.Sprintf("temporality = '%s'", mq.Temporality))
|
||||
if constants.IsDotMetricsEnabled {
|
||||
conditions = append(conditions, "__normalized = false")
|
||||
@@ -371,7 +371,7 @@ func PrepareTimeseriesFilterQueryV3(start, end int64, mq *v3.BuilderQuery) (stri
|
||||
if op == v3.FilterOperatorContains || op == v3.FilterOperatorNotContains {
|
||||
toFormat = fmt.Sprintf("%%%s%%", toFormat)
|
||||
}
|
||||
fmtVal := utils.ClickHouseFormattedValue(toFormat)
|
||||
fmtVal := utils.DatastoreFormattedValue(toFormat)
|
||||
switch op {
|
||||
case v3.FilterOperatorEqual:
|
||||
conditions = append(conditions, fmt.Sprintf("JSONExtractString(labels, '%s') = %s", item.Key.Key, fmtVal))
|
||||
|
||||
@@ -93,7 +93,7 @@ func (receiver *SummaryService) GetMetricsSummary(ctx context.Context, orgID val
|
||||
g.Go(func() error {
|
||||
metadata, err := receiver.reader.GetMetricMetadata(ctx, orgID, metricName, metricName)
|
||||
if err != nil {
|
||||
return &model.ApiError{Typ: "ClickHouseError", Err: err}
|
||||
return &model.ApiError{Typ: "DatastoreError", Err: err}
|
||||
}
|
||||
metricDetailsDTO.Name = metricName
|
||||
metricDetailsDTO.Unit = metadata.Unit
|
||||
|
||||
@@ -1,146 +1,30 @@
|
||||
//go:build grpc
|
||||
// UpsertControlProcessors is the OPAmp ingestion-rules update path,
|
||||
// used to push processor config to remote agents. The real
|
||||
// implementation pulls go.opentelemetry.io/collector/confmap which
|
||||
// transitively requires google.golang.org/grpc — that path is gated
|
||||
// behind -tags grpc.
|
||||
//
|
||||
// Default builds expose a no-op that returns "" with no error so
|
||||
// callers in pkg/query-service/agentConf compile without dragging the
|
||||
// collector framework into the dep graph.
|
||||
|
||||
package opamp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"errors"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/errors"
|
||||
model "github.com/hanzoai/o11y/pkg/query-service/app/opamp/model"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/opamp/otelconfig"
|
||||
"github.com/knadh/koanf/parsers/yaml"
|
||||
"github.com/open-telemetry/opamp-go/protobufs"
|
||||
"go.opentelemetry.io/collector/confmap"
|
||||
"go.uber.org/zap"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/opamp/model"
|
||||
)
|
||||
|
||||
var (
|
||||
CodeNoAgentsAvailable = errors.MustNewCode("no_agents_available")
|
||||
CodeOpAmpServerDown = errors.MustNewCode("opamp_server_down")
|
||||
CodeMultipleAgentsNotSupported = errors.MustNewCode("multiple_agents_not_supported")
|
||||
)
|
||||
// errOpAmpDisabled signals that OPAmp remote-agent control is not
|
||||
// available in this build. Wired in pkg/query-service/agentConf via
|
||||
// the OnChangeCallback contract.
|
||||
var errOpAmpDisabled = errors.New("opamp: ingestion-rules push disabled (rebuild with -tags grpc to enable)")
|
||||
|
||||
// inserts or updates ingestion controller processors depending
|
||||
// on the signal (metrics or traces)
|
||||
func UpsertControlProcessors(ctx context.Context, signal string,
|
||||
processors map[string]interface{}, callback model.OnChangeCallback,
|
||||
) (string, error) {
|
||||
// note: only processors enabled through tracesPipelinePlan will be added
|
||||
// to pipeline. To enable or disable processors from pipeline, call
|
||||
// AddToTracePipeline() or RemoveFromTracesPipeline() prior to calling
|
||||
// this method
|
||||
|
||||
zap.L().Debug("initiating ingestion rules deployment config", zap.String("signal", signal), zap.Any("processors", processors))
|
||||
|
||||
if signal != string(Metrics) && signal != string(Traces) {
|
||||
zap.L().Error("received invalid signal int UpsertControlProcessors", zap.String("signal", signal))
|
||||
return "", errors.NewInvalidInputf(errors.CodeInvalidInput, "signal not supported in ingestion rules: %s", signal)
|
||||
}
|
||||
|
||||
if opAmpServer == nil {
|
||||
return "", errors.NewInternalf(CodeOpAmpServerDown, "opamp server is down, unable to push config to agent at this moment")
|
||||
}
|
||||
|
||||
agents := opAmpServer.agents.GetAllAgents()
|
||||
if len(agents) == 0 {
|
||||
return "", errors.NewInternalf(CodeNoAgentsAvailable, "no agents available at the moment")
|
||||
}
|
||||
|
||||
if len(agents) > 1 && signal == string(Traces) {
|
||||
zap.L().Debug("found multiple agents. this feature is not supported for traces pipeline (sampling rules)")
|
||||
return "", errors.NewInvalidInputf(CodeMultipleAgentsNotSupported, "multiple agents not supported in sampling rules")
|
||||
}
|
||||
hash := ""
|
||||
for _, agent := range agents {
|
||||
agenthash, err := addIngestionControlToAgent(agent, signal, processors, false)
|
||||
if err != nil {
|
||||
zap.L().Error("failed to push ingestion rules config to agent", zap.String("agentID", agent.AgentID), zap.Error(err))
|
||||
continue
|
||||
}
|
||||
|
||||
if agenthash != "" {
|
||||
// subscribe callback
|
||||
model.ListenToConfigUpdate(agent.OrgID, agent.AgentID, agenthash, callback)
|
||||
}
|
||||
|
||||
hash = agenthash
|
||||
}
|
||||
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
// addIngestionControlToAgent adds ingestion contorl rules to agent config
|
||||
func addIngestionControlToAgent(agent *model.Agent, signal string, processors map[string]interface{}, withLB bool) (string, error) {
|
||||
confHash := ""
|
||||
config := agent.Config
|
||||
c, err := yaml.Parser().Unmarshal([]byte(config))
|
||||
if err != nil {
|
||||
return confHash, err
|
||||
}
|
||||
|
||||
agentConf := confmap.NewFromStringMap(c)
|
||||
|
||||
// add ingestion control spec
|
||||
err = makeIngestionControlSpec(agentConf, Signal(signal), processors)
|
||||
if err != nil {
|
||||
zap.L().Error("failed to prepare ingestion control processors for agent", zap.String("agentID", agent.AgentID), zap.Error(err))
|
||||
return confHash, err
|
||||
}
|
||||
|
||||
// ------ complete adding processor
|
||||
configR, err := yaml.Parser().Marshal(agentConf.ToStringMap())
|
||||
if err != nil {
|
||||
return confHash, err
|
||||
}
|
||||
|
||||
zap.L().Debug("sending new config", zap.String("config", string(configR)))
|
||||
hash := sha256.New()
|
||||
_, err = hash.Write(configR)
|
||||
if err != nil {
|
||||
return confHash, err
|
||||
}
|
||||
confHash = string(hash.Sum(nil))
|
||||
agent.Config = string(configR)
|
||||
err = agent.Upsert()
|
||||
if err != nil {
|
||||
return confHash, err
|
||||
}
|
||||
|
||||
agent.SendToAgent(&protobufs.ServerToAgent{
|
||||
RemoteConfig: &protobufs.AgentRemoteConfig{
|
||||
Config: &protobufs.AgentConfigMap{
|
||||
ConfigMap: map[string]*protobufs.AgentConfigFile{
|
||||
"collector.yaml": {
|
||||
Body: configR,
|
||||
ContentType: "application/x-yaml",
|
||||
},
|
||||
},
|
||||
},
|
||||
ConfigHash: []byte(confHash),
|
||||
},
|
||||
})
|
||||
|
||||
return string(confHash), nil
|
||||
}
|
||||
|
||||
// prepare spec to introduce ingestion control in agent conf
|
||||
func makeIngestionControlSpec(agentConf *confmap.Conf, signal Signal, processors map[string]interface{}) error {
|
||||
configParser := otelconfig.NewConfigParser(agentConf)
|
||||
configParser.UpdateProcessors(processors)
|
||||
|
||||
// edit pipeline if processor is missing
|
||||
currentPipeline := configParser.PipelineProcessors(string(signal))
|
||||
|
||||
// merge tracesPipelinePlan with current pipeline
|
||||
mergedPipeline, err := buildPipeline(signal, currentPipeline)
|
||||
if err != nil {
|
||||
zap.L().Error("failed to build pipeline", zap.String("signal", string(signal)), zap.Error(err))
|
||||
return err
|
||||
}
|
||||
|
||||
// add merged pipeline to the service
|
||||
configParser.UpdateProcsInPipeline(string(signal), mergedPipeline)
|
||||
|
||||
return nil
|
||||
// UpsertControlProcessors is a no-op for grpc-untagged builds.
|
||||
// Returns an empty config hash and the disabled-sentinel error so the
|
||||
// caller can decide whether to surface it.
|
||||
func UpsertControlProcessors(_ context.Context, _ string, _ map[string]interface{}, _ model.OnChangeCallback) (string, error) {
|
||||
return "", errOpAmpDisabled
|
||||
}
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
//go:build !grpc
|
||||
|
||||
// UpsertControlProcessors is the OPAmp ingestion-rules update path,
|
||||
// used to push processor config to remote agents. The real
|
||||
// implementation pulls go.opentelemetry.io/collector/confmap which
|
||||
// transitively requires google.golang.org/grpc — that path is gated
|
||||
// behind -tags grpc.
|
||||
//
|
||||
// Default builds expose a no-op that returns "" with no error so
|
||||
// callers in pkg/query-service/agentConf compile without dragging the
|
||||
// collector framework into the dep graph.
|
||||
|
||||
package opamp
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/opamp/model"
|
||||
)
|
||||
|
||||
// errOpAmpDisabled signals that OPAmp remote-agent control is not
|
||||
// available in this build. Wired in pkg/query-service/agentConf via
|
||||
// the OnChangeCallback contract.
|
||||
var errOpAmpDisabled = errors.New("opamp: ingestion-rules push disabled (rebuild with -tags grpc to enable)")
|
||||
|
||||
// UpsertControlProcessors is a no-op for grpc-untagged builds.
|
||||
// Returns an empty config hash and the disabled-sentinel error so the
|
||||
// caller can decide whether to surface it.
|
||||
func UpsertControlProcessors(_ context.Context, _ string, _ map[string]interface{}, _ model.OnChangeCallback) (string, error) {
|
||||
return "", errOpAmpDisabled
|
||||
}
|
||||
@@ -1,197 +0,0 @@
|
||||
//go:build grpc
|
||||
|
||||
package otelconfig
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/collector/confmap"
|
||||
)
|
||||
|
||||
type ConfigParser struct {
|
||||
lock sync.Mutex
|
||||
agentConf *confmap.Conf
|
||||
}
|
||||
|
||||
func NewConfigParser(agentConf *confmap.Conf) ConfigParser {
|
||||
return ConfigParser{
|
||||
agentConf: agentConf,
|
||||
}
|
||||
}
|
||||
|
||||
func toMap(i interface{}) map[string]interface{} {
|
||||
return i.(map[string]interface{})
|
||||
}
|
||||
|
||||
func toList(i interface{}) []interface{} {
|
||||
return i.([]interface{})
|
||||
}
|
||||
|
||||
// sent when key is not found in config
|
||||
func emptyMap() map[string]interface{} {
|
||||
return map[string]interface{}{}
|
||||
}
|
||||
|
||||
func emptyList() []interface{} {
|
||||
return []interface{}{}
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Service() map[string]interface{} {
|
||||
service := cp.agentConf.Get("service")
|
||||
if service == nil {
|
||||
return emptyMap()
|
||||
}
|
||||
return toMap(service)
|
||||
}
|
||||
|
||||
// components gets the high level parts like receivers, exporters, processors etc
|
||||
func (cp *ConfigParser) components(partName, nameOptional string) map[string]interface{} {
|
||||
parts := cp.agentConf.Get(partName)
|
||||
if parts == nil {
|
||||
return emptyMap()
|
||||
}
|
||||
|
||||
parsedParts := toMap(parts)
|
||||
if nameOptional != "" {
|
||||
if p, ok := parsedParts[nameOptional]; ok {
|
||||
return p.(map[string]interface{})
|
||||
} else {
|
||||
return emptyMap()
|
||||
}
|
||||
}
|
||||
|
||||
return parsedParts
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Processors() map[string]interface{} {
|
||||
return cp.components("processors", "")
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Processor(name string) map[string]interface{} {
|
||||
return cp.components("processors", name)
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Exporters() map[string]interface{} {
|
||||
return cp.components("exporters", "")
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Exporter(name string) map[string]interface{} {
|
||||
return cp.components("exporters", name)
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Receivers() map[string]interface{} {
|
||||
return cp.components("receivers", "")
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Receiver(name string) map[string]interface{} {
|
||||
return cp.components("receivers", name)
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Pipelines(nameOptional string) map[string]interface{} {
|
||||
services := cp.Service()
|
||||
if p, ok := services["pipelines"]; ok {
|
||||
pipelines := toMap(p)
|
||||
if nameOptional != "" {
|
||||
if namedPipeline, ok := pipelines[nameOptional]; ok {
|
||||
return toMap(namedPipeline)
|
||||
} else {
|
||||
return emptyMap()
|
||||
}
|
||||
|
||||
}
|
||||
return pipelines
|
||||
}
|
||||
return emptyMap()
|
||||
}
|
||||
|
||||
// component can be "recevers", "exporter" or "processors"
|
||||
func (cp *ConfigParser) PipelineComponent(pipelineName, pipelineComponent string) []interface{} {
|
||||
pipeline := cp.Pipelines(pipelineName)
|
||||
if exporters, ok := pipeline[pipelineComponent]; ok {
|
||||
exporters := toList(exporters)
|
||||
return exporters
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) PipelineExporters(pipelineName string) []interface{} {
|
||||
return cp.PipelineComponent(pipelineName, "exporters")
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) PipelineReceivers(pipelineName string) []interface{} {
|
||||
return cp.PipelineComponent(pipelineName, "receivers")
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) PipelineProcessors(pipelineName string) []interface{} {
|
||||
return cp.PipelineComponent(pipelineName, "processors")
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) CheckPipelineExists(name string) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
pipelines := cp.Pipelines(name)
|
||||
return len(pipelines) > 0
|
||||
}
|
||||
|
||||
// CheckEntryInPipeline lets you look for an entry in pipeline by receiver, processor or exporter name
|
||||
func (cp *ConfigParser) CheckEntryInPipeline(pipelineName, pipelineComponent, name string) bool {
|
||||
if pipelineName == "" || pipelineComponent == "" || name == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
list := cp.PipelineComponent(pipelineName, pipelineComponent)
|
||||
var found bool
|
||||
for _, item := range list {
|
||||
if item == name {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) CheckExporterInPipeline(pipelineName, name string) bool {
|
||||
return cp.CheckEntryInPipeline(pipelineName, "exporters", name)
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) CheckProcessorInPipeline(pipelineName, name string) bool {
|
||||
return cp.CheckEntryInPipeline(pipelineName, "processors", name)
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) Merge(c *confmap.Conf) {
|
||||
cp.lock.Lock()
|
||||
defer cp.lock.Unlock()
|
||||
_ = cp.agentConf.Merge(c)
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) UpdateProcessors(processors map[string]interface{}) {
|
||||
updates := cp.Processors()
|
||||
|
||||
for key, params := range processors {
|
||||
updates[key] = params
|
||||
}
|
||||
|
||||
updatedProcessors := map[string]interface{}{
|
||||
"processors": updates,
|
||||
}
|
||||
|
||||
updatedProcessorConf := confmap.NewFromStringMap(updatedProcessors)
|
||||
|
||||
cp.Merge(updatedProcessorConf)
|
||||
}
|
||||
|
||||
func (cp *ConfigParser) UpdateProcsInPipeline(pipelineName string, list []interface{}) {
|
||||
|
||||
serviceConf := map[string]interface{}{
|
||||
"service": map[string]interface{}{
|
||||
"pipelines": map[string]interface{}{
|
||||
pipelineName: map[string]interface{}{
|
||||
"processors": list,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
cp.Merge(confmap.NewFromStringMap(serviceConf))
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
//go:build grpc
|
||||
|
||||
package otelconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/knadh/koanf/parsers/yaml"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.opentelemetry.io/collector/confmap"
|
||||
)
|
||||
|
||||
func TestServiceConfig(t *testing.T) {
|
||||
yamlFile, err := os.ReadFile("./testdata/service.yaml")
|
||||
if err != nil {
|
||||
fmt.Printf("yamlFile.Get err #%v ", err)
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
c, err := yaml.Parser().Unmarshal([]byte(yamlFile))
|
||||
if err != nil {
|
||||
fmt.Println("failed to parse config file as yaml", err)
|
||||
t.Fail()
|
||||
return
|
||||
}
|
||||
|
||||
agentConf := confmap.NewFromStringMap(c)
|
||||
configParser := NewConfigParser(agentConf)
|
||||
|
||||
expected := map[string]interface{}{
|
||||
"extensions": []interface{}{"zpages"},
|
||||
"pipelines": map[string]interface{}{
|
||||
"traces": map[string]interface{}{
|
||||
"receivers": []interface{}{"jaeger", "otlp"},
|
||||
"processors": []interface{}{
|
||||
"o11yspanmetrics/cumulative", "batch",
|
||||
},
|
||||
"exporters": []interface{}{
|
||||
"clickhousetraces",
|
||||
},
|
||||
},
|
||||
"metrics": map[string]interface{}{
|
||||
"receivers": []interface{}{
|
||||
"otlp", "hostmetrics",
|
||||
},
|
||||
"processors": []interface{}{
|
||||
"batch",
|
||||
},
|
||||
"exporters": []interface{}{
|
||||
"clickhousemetricswrite",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
require.Equal(t, expected, configParser.Service(), "expected same service config after parsing")
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import (
|
||||
"github.com/hanzoai/o11y/pkg/query-service/postprocess"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/utils"
|
||||
querytemplate "github.com/hanzoai/o11y/pkg/query-service/utils/queryTemplate"
|
||||
chVariables "github.com/hanzoai/o11y/pkg/variables/clickhouse"
|
||||
chVariables "github.com/hanzoai/o11y/pkg/variables/datastore"
|
||||
)
|
||||
|
||||
var allowedFunctions = []string{"count", "ratePerSec", "sum", "avg", "min", "max", "p50", "p90", "p95", "p99"}
|
||||
@@ -722,7 +722,7 @@ func validateExpressions(expressions []string, funcs map[string]govaluate.Expres
|
||||
return errs
|
||||
}
|
||||
|
||||
// chTransformQuery transforms the clickhouse query with the given variables
|
||||
// chTransformQuery transforms the datastore query with the given variables
|
||||
// it is used to check what would be the query if variables are selected as __all__.
|
||||
// for now, this is just a pass through, but in the future, we will use it to
|
||||
// dashboard variables
|
||||
@@ -740,9 +740,9 @@ func chTransformQuery(query string, variables map[string]interface{}) {
|
||||
transformer := chVariables.NewQueryTransformer(query, varsForTransform)
|
||||
transformedQuery, err := transformer.Transform()
|
||||
if err != nil {
|
||||
zap.L().Warn("failed to transform clickhouse query", zap.String("query", query), zap.Error(err))
|
||||
zap.L().Warn("failed to transform datastore query", zap.String("query", query), zap.Error(err))
|
||||
}
|
||||
zap.L().Info("transformed clickhouse query", zap.String("transformedQuery", transformedQuery), zap.String("originalQuery", query))
|
||||
zap.L().Info("transformed datastore query", zap.String("transformedQuery", transformedQuery), zap.String("originalQuery", query))
|
||||
}
|
||||
|
||||
func ParseQueryRangeParams(r *http.Request) (*v3.QueryRangeParamsV3, *model.ApiError) {
|
||||
@@ -767,8 +767,8 @@ func ParseQueryRangeParams(r *http.Request) (*v3.QueryRangeParamsV3, *model.ApiE
|
||||
for name, value := range queryRangeParams.Variables {
|
||||
if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypePromQL {
|
||||
formattedVars[name] = metrics.PromFormattedValue(value)
|
||||
} else if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
|
||||
formattedVars[name] = utils.ClickHouseFormattedValue(value)
|
||||
} else if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeDatastoreSQL {
|
||||
formattedVars[name] = utils.DatastoreFormattedValue(value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -876,9 +876,9 @@ func ParseQueryRangeParams(r *http.Request) (*v3.QueryRangeParamsV3, *model.ApiE
|
||||
queryRangeParams.Start = queryRangeParams.End
|
||||
}
|
||||
|
||||
// replace go template variables in clickhouse query
|
||||
if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
|
||||
for _, chQuery := range queryRangeParams.CompositeQuery.ClickHouseQueries {
|
||||
// replace go template variables in datastore query
|
||||
if queryRangeParams.CompositeQuery.QueryType == v3.QueryTypeDatastoreSQL {
|
||||
for _, chQuery := range queryRangeParams.CompositeQuery.DatastoreQueries {
|
||||
if chQuery.Disabled {
|
||||
continue
|
||||
}
|
||||
@@ -903,7 +903,7 @@ func ParseQueryRangeParams(r *http.Request) (*v3.QueryRangeParamsV3, *model.ApiE
|
||||
chQuery.Query = strings.Replace(chQuery.Query, fmt.Sprintf("$%s", k), fmt.Sprint(queryRangeParams.Variables[k]), -1)
|
||||
}
|
||||
|
||||
tmpl := template.New("clickhouse-query")
|
||||
tmpl := template.New("datastore-query")
|
||||
tmpl, err := tmpl.Parse(chQuery.Query)
|
||||
if err != nil {
|
||||
return nil, &model.ApiError{Typ: model.ErrorBadData, Err: err}
|
||||
|
||||
@@ -337,7 +337,7 @@ func TestParseQueryRangeParamsCompositeQuery(t *testing.T) {
|
||||
desc: "no query in request",
|
||||
compositeQuery: v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
},
|
||||
expectErr: true,
|
||||
errMsg: "composite query must contain at least one query",
|
||||
@@ -346,8 +346,8 @@ func TestParseQueryRangeParamsCompositeQuery(t *testing.T) {
|
||||
desc: "invalid panel type",
|
||||
compositeQuery: v3.CompositeQuery{
|
||||
PanelType: "invalid",
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
DatastoreQueries: map[string]*v3.DatastoreQuery{
|
||||
"A": {
|
||||
Query: "query",
|
||||
Disabled: false,
|
||||
@@ -362,7 +362,7 @@ func TestParseQueryRangeParamsCompositeQuery(t *testing.T) {
|
||||
compositeQuery: v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
QueryType: "invalid",
|
||||
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
|
||||
DatastoreQueries: map[string]*v3.DatastoreQuery{
|
||||
"A": {
|
||||
Query: "query",
|
||||
Disabled: false,
|
||||
@@ -388,11 +388,11 @@ func TestParseQueryRangeParamsCompositeQuery(t *testing.T) {
|
||||
errMsg: "query is empty",
|
||||
},
|
||||
{
|
||||
desc: "invalid clickhouse query",
|
||||
desc: "invalid datastore query",
|
||||
compositeQuery: v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
DatastoreQueries: map[string]*v3.DatastoreQuery{
|
||||
"A": {
|
||||
Query: "",
|
||||
Disabled: false,
|
||||
@@ -418,11 +418,11 @@ func TestParseQueryRangeParamsCompositeQuery(t *testing.T) {
|
||||
errMsg: "query is empty",
|
||||
},
|
||||
{
|
||||
desc: "invalid clickhouse query with disabled query",
|
||||
desc: "invalid datastore query with disabled query",
|
||||
compositeQuery: v3.CompositeQuery{
|
||||
PanelType: v3.PanelTypeGraph,
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
DatastoreQueries: map[string]*v3.DatastoreQuery{
|
||||
"A": {
|
||||
Query: "",
|
||||
Disabled: true,
|
||||
|
||||
@@ -103,7 +103,7 @@ func (q *querier) runBuilderQuery(
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: nil}
|
||||
return
|
||||
}
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: series}
|
||||
return
|
||||
}
|
||||
@@ -118,7 +118,7 @@ func (q *querier) runBuilderQuery(
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: nil}
|
||||
return
|
||||
}
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
if err != nil {
|
||||
ch <- channelResult{
|
||||
Err: err,
|
||||
@@ -208,7 +208,7 @@ func (q *querier) runBuilderQuery(
|
||||
}
|
||||
}
|
||||
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: series}
|
||||
return
|
||||
}
|
||||
@@ -223,7 +223,7 @@ func (q *querier) runBuilderQuery(
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: nil}
|
||||
return
|
||||
}
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: series}
|
||||
return
|
||||
}
|
||||
@@ -250,7 +250,7 @@ func (q *querier) runBuilderQuery(
|
||||
}
|
||||
return
|
||||
}
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
if err != nil {
|
||||
ch <- channelResult{
|
||||
Err: err,
|
||||
@@ -299,7 +299,7 @@ func (q *querier) runBuilderExpression(
|
||||
if _, ok := cacheKeys[queryName]; !ok || params.NoCache {
|
||||
zap.L().Info("skipping cache for expression query", zap.String("queryName", queryName), zap.Int64("start", params.Start), zap.Int64("end", params.End), zap.Int64("step", params.Step), zap.Bool("noCache", params.NoCache), zap.String("cacheKey", cacheKeys[queryName]))
|
||||
query := queries[queryName]
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: series}
|
||||
return
|
||||
}
|
||||
@@ -319,7 +319,7 @@ func (q *querier) runBuilderExpression(
|
||||
Variables: params.Variables,
|
||||
})
|
||||
query := missQueries[queryName]
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
if err != nil {
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: nil}
|
||||
return
|
||||
|
||||
@@ -91,7 +91,7 @@ func NewQuerier(opts QuerierOptions) interfaces.Querier {
|
||||
}
|
||||
}
|
||||
|
||||
func (q *querier) execClickHouseQuery(ctx context.Context, query string) ([]*v3.Series, error) {
|
||||
func (q *querier) execDatastoreQuery(ctx context.Context, query string) ([]*v3.Series, error) {
|
||||
q.queriesExecuted = append(q.queriesExecuted, query)
|
||||
if q.testingMode && q.reader == nil {
|
||||
return q.returnedSeries, q.returnedErr
|
||||
@@ -261,17 +261,17 @@ func (q *querier) runPromQueries(ctx context.Context, orgID valuer.UUID, params
|
||||
return results, errQueriesByName, err
|
||||
}
|
||||
|
||||
func (q *querier) runClickHouseQueries(ctx context.Context, params *v3.QueryRangeParamsV3) ([]*v3.Result, map[string]error, error) {
|
||||
channelResults := make(chan channelResult, len(params.CompositeQuery.ClickHouseQueries))
|
||||
func (q *querier) runDatastoreQueries(ctx context.Context, params *v3.QueryRangeParamsV3) ([]*v3.Result, map[string]error, error) {
|
||||
channelResults := make(chan channelResult, len(params.CompositeQuery.DatastoreQueries))
|
||||
var wg sync.WaitGroup
|
||||
for queryName, clickHouseQuery := range params.CompositeQuery.ClickHouseQueries {
|
||||
for queryName, clickHouseQuery := range params.CompositeQuery.DatastoreQueries {
|
||||
if clickHouseQuery.Disabled {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(queryName string, clickHouseQuery *v3.ClickHouseQuery) {
|
||||
go func(queryName string, clickHouseQuery *v3.DatastoreQuery) {
|
||||
defer wg.Done()
|
||||
series, err := q.execClickHouseQuery(ctx, clickHouseQuery.Query)
|
||||
series, err := q.execDatastoreQuery(ctx, clickHouseQuery.Query)
|
||||
channelResults <- channelResult{Err: err, Name: queryName, Query: clickHouseQuery.Query, Series: series}
|
||||
}(queryName, clickHouseQuery)
|
||||
}
|
||||
@@ -296,7 +296,7 @@ func (q *querier) runClickHouseQueries(ctx context.Context, params *v3.QueryRang
|
||||
|
||||
var err error
|
||||
if len(errs) > 0 {
|
||||
err = fmt.Errorf("error in clickhouse queries")
|
||||
err = fmt.Errorf("error in datastore queries")
|
||||
}
|
||||
return results, errQueriesByName, err
|
||||
}
|
||||
@@ -447,8 +447,8 @@ func (q *querier) runBuilderListQueries(ctx context.Context, params *v3.QueryRan
|
||||
var err error
|
||||
if params.CompositeQuery.QueryType == v3.QueryTypeBuilder {
|
||||
queries, err = q.builder.PrepareQueries(params)
|
||||
} else if params.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
|
||||
for name, chQuery := range params.CompositeQuery.ClickHouseQueries {
|
||||
} else if params.CompositeQuery.QueryType == v3.QueryTypeDatastoreSQL {
|
||||
for name, chQuery := range params.CompositeQuery.DatastoreQueries {
|
||||
queries[name] = chQuery.Query
|
||||
}
|
||||
}
|
||||
@@ -519,12 +519,12 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, params *v3.
|
||||
}
|
||||
case v3.QueryTypePromQL:
|
||||
results, errQueriesByName, err = q.runPromQueries(ctx, orgID, params)
|
||||
case v3.QueryTypeClickHouseSQL:
|
||||
case v3.QueryTypeDatastoreSQL:
|
||||
ctx = context.WithValue(ctx, "enforce_max_result_rows", true)
|
||||
if params.CompositeQuery.PanelType == v3.PanelTypeList || params.CompositeQuery.PanelType == v3.PanelTypeTrace {
|
||||
results, errQueriesByName, err = q.runBuilderListQueries(ctx, params)
|
||||
} else {
|
||||
results, errQueriesByName, err = q.runClickHouseQueries(ctx, params)
|
||||
results, errQueriesByName, err = q.runDatastoreQueries(ctx, params)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("invalid query type")
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus/prometheustest"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/clickhouseReader"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/datastoreReader"
|
||||
logsV4 "github.com/hanzoai/o11y/pkg/query-service/app/logs/v4"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/queryBuilder"
|
||||
tracesV3 "github.com/hanzoai/o11y/pkg/query-service/app/traces/v3"
|
||||
@@ -1380,13 +1380,13 @@ func Test_querier_Traces_runWindowBasedListQueryDesc(t *testing.T) {
|
||||
}
|
||||
testName := "name"
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
|
||||
// iterate over test data, create reader and run test
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "datastore"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Configure mock responses
|
||||
for _, response := range tc.queryResponses {
|
||||
@@ -1402,7 +1402,7 @@ func Test_querier_Traces_runWindowBasedListQueryDesc(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create reader and querier
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore),
|
||||
@@ -1607,13 +1607,13 @@ func Test_querier_Traces_runWindowBasedListQueryAsc(t *testing.T) {
|
||||
}
|
||||
testName := "name"
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
|
||||
// iterate over test data, create reader and run test
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "datastore"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Configure mock responses
|
||||
for _, response := range tc.queryResponses {
|
||||
@@ -1627,7 +1627,7 @@ func Test_querier_Traces_runWindowBasedListQueryAsc(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create reader and querier
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore),
|
||||
@@ -1907,13 +1907,13 @@ func Test_querier_Logs_runWindowBasedListQueryDesc(t *testing.T) {
|
||||
}
|
||||
testName := "name"
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
|
||||
// iterate over test data, create reader and run test
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "datastore"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Configure mock responses
|
||||
for _, response := range tc.queryResponses {
|
||||
@@ -1927,7 +1927,7 @@ func Test_querier_Logs_runWindowBasedListQueryDesc(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create reader and querier
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore),
|
||||
@@ -2134,13 +2134,13 @@ func Test_querier_Logs_runWindowBasedListQueryAsc(t *testing.T) {
|
||||
}
|
||||
testName := "name"
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
|
||||
// iterate over test data, create reader and run test
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "datastore"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Configure mock responses
|
||||
for _, response := range tc.queryResponses {
|
||||
@@ -2154,7 +2154,7 @@ func Test_querier_Logs_runWindowBasedListQueryAsc(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create reader and querier
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore),
|
||||
|
||||
@@ -105,7 +105,7 @@ func (q *querier) runBuilderQuery(
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: nil}
|
||||
return
|
||||
}
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: series}
|
||||
return
|
||||
}
|
||||
@@ -119,7 +119,7 @@ func (q *querier) runBuilderQuery(
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: nil}
|
||||
return
|
||||
}
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
if err != nil {
|
||||
ch <- channelResult{
|
||||
Err: err,
|
||||
@@ -210,7 +210,7 @@ func (q *querier) runBuilderQuery(
|
||||
}
|
||||
}
|
||||
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: series}
|
||||
return
|
||||
}
|
||||
@@ -225,7 +225,7 @@ func (q *querier) runBuilderQuery(
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: nil}
|
||||
return
|
||||
}
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
ch <- channelResult{Err: err, Name: queryName, Query: query, Series: series}
|
||||
return
|
||||
}
|
||||
@@ -251,7 +251,7 @@ func (q *querier) runBuilderQuery(
|
||||
}
|
||||
return
|
||||
}
|
||||
series, err := q.execClickHouseQuery(ctx, query)
|
||||
series, err := q.execDatastoreQuery(ctx, query)
|
||||
if err != nil {
|
||||
ch <- channelResult{
|
||||
Err: err,
|
||||
|
||||
@@ -91,9 +91,9 @@ func NewQuerier(opts QuerierOptions) interfaces.Querier {
|
||||
}
|
||||
}
|
||||
|
||||
// execClickHouseQuery executes the clickhouse query and returns the series list
|
||||
// execDatastoreQuery executes the datastore query and returns the series list
|
||||
// if testing mode is enabled, it returns the mocked series list
|
||||
func (q *querier) execClickHouseQuery(ctx context.Context, query string) ([]*v3.Series, error) {
|
||||
func (q *querier) execDatastoreQuery(ctx context.Context, query string) ([]*v3.Series, error) {
|
||||
if q.testingMode && q.reader == nil {
|
||||
q.queriesExecuted = append(q.queriesExecuted, query)
|
||||
return q.returnedSeries, q.returnedErr
|
||||
@@ -262,17 +262,17 @@ func (q *querier) runPromQueries(ctx context.Context, orgID valuer.UUID, params
|
||||
return results, errQueriesByName, err
|
||||
}
|
||||
|
||||
func (q *querier) runClickHouseQueries(ctx context.Context, params *v3.QueryRangeParamsV3) ([]*v3.Result, map[string]error, error) {
|
||||
channelResults := make(chan channelResult, len(params.CompositeQuery.ClickHouseQueries))
|
||||
func (q *querier) runDatastoreQueries(ctx context.Context, params *v3.QueryRangeParamsV3) ([]*v3.Result, map[string]error, error) {
|
||||
channelResults := make(chan channelResult, len(params.CompositeQuery.DatastoreQueries))
|
||||
var wg sync.WaitGroup
|
||||
for queryName, clickHouseQuery := range params.CompositeQuery.ClickHouseQueries {
|
||||
for queryName, clickHouseQuery := range params.CompositeQuery.DatastoreQueries {
|
||||
if clickHouseQuery.Disabled {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(queryName string, clickHouseQuery *v3.ClickHouseQuery) {
|
||||
go func(queryName string, clickHouseQuery *v3.DatastoreQuery) {
|
||||
defer wg.Done()
|
||||
series, err := q.execClickHouseQuery(ctx, clickHouseQuery.Query)
|
||||
series, err := q.execDatastoreQuery(ctx, clickHouseQuery.Query)
|
||||
channelResults <- channelResult{Err: err, Name: queryName, Query: clickHouseQuery.Query, Series: series}
|
||||
}(queryName, clickHouseQuery)
|
||||
}
|
||||
@@ -297,7 +297,7 @@ func (q *querier) runClickHouseQueries(ctx context.Context, params *v3.QueryRang
|
||||
|
||||
var err error
|
||||
if len(errs) > 0 {
|
||||
err = fmt.Errorf("error in clickhouse queries")
|
||||
err = fmt.Errorf("error in datastore queries")
|
||||
}
|
||||
return results, errQueriesByName, err
|
||||
}
|
||||
@@ -449,8 +449,8 @@ func (q *querier) runBuilderListQueries(ctx context.Context, params *v3.QueryRan
|
||||
var err error
|
||||
if params.CompositeQuery.QueryType == v3.QueryTypeBuilder {
|
||||
queries, err = q.builder.PrepareQueries(params)
|
||||
} else if params.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
|
||||
for name, chQuery := range params.CompositeQuery.ClickHouseQueries {
|
||||
} else if params.CompositeQuery.QueryType == v3.QueryTypeDatastoreSQL {
|
||||
for name, chQuery := range params.CompositeQuery.DatastoreQueries {
|
||||
queries[name] = chQuery.Query
|
||||
}
|
||||
}
|
||||
@@ -526,12 +526,12 @@ func (q *querier) QueryRange(ctx context.Context, orgID valuer.UUID, params *v3.
|
||||
}
|
||||
case v3.QueryTypePromQL:
|
||||
results, errQueriesByName, err = q.runPromQueries(ctx, orgID, params)
|
||||
case v3.QueryTypeClickHouseSQL:
|
||||
case v3.QueryTypeDatastoreSQL:
|
||||
if params.CompositeQuery.PanelType == v3.PanelTypeList || params.CompositeQuery.PanelType == v3.PanelTypeTrace {
|
||||
results, errQueriesByName, err = q.runBuilderListQueries(ctx, params)
|
||||
} else {
|
||||
ctx = context.WithValue(ctx, "enforce_max_result_rows", true)
|
||||
results, errQueriesByName, err = q.runClickHouseQueries(ctx, params)
|
||||
results, errQueriesByName, err = q.runDatastoreQueries(ctx, params)
|
||||
}
|
||||
default:
|
||||
err = fmt.Errorf("invalid query type")
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus/prometheustest"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/clickhouseReader"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/datastoreReader"
|
||||
logsV4 "github.com/hanzoai/o11y/pkg/query-service/app/logs/v4"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/queryBuilder"
|
||||
tracesV3 "github.com/hanzoai/o11y/pkg/query-service/app/traces/v3"
|
||||
@@ -1433,13 +1433,13 @@ func Test_querier_Traces_runWindowBasedListQueryDesc(t *testing.T) {
|
||||
}
|
||||
testName := "name"
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
|
||||
// iterate over test data, create reader and run test
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "datastore"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Configure mock responses
|
||||
for _, response := range tc.queryResponses {
|
||||
@@ -1454,7 +1454,7 @@ func Test_querier_Traces_runWindowBasedListQueryDesc(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create reader and querier
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore),
|
||||
@@ -1659,13 +1659,13 @@ func Test_querier_Traces_runWindowBasedListQueryAsc(t *testing.T) {
|
||||
}
|
||||
testName := "name"
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
|
||||
// iterate over test data, create reader and run test
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "datastore"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Configure mock responses
|
||||
for _, response := range tc.queryResponses {
|
||||
@@ -1679,7 +1679,7 @@ func Test_querier_Traces_runWindowBasedListQueryAsc(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create reader and querier
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore),
|
||||
@@ -1958,13 +1958,13 @@ func Test_querier_Logs_runWindowBasedListQueryDesc(t *testing.T) {
|
||||
}
|
||||
testName := "name"
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
|
||||
// iterate over test data, create reader and run test
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "datastore"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Configure mock responses
|
||||
for _, response := range tc.queryResponses {
|
||||
@@ -1978,7 +1978,7 @@ func Test_querier_Logs_runWindowBasedListQueryDesc(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create reader and querier
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore),
|
||||
@@ -2185,13 +2185,13 @@ func Test_querier_Logs_runWindowBasedListQueryAsc(t *testing.T) {
|
||||
}
|
||||
testName := "name"
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
|
||||
// iterate over test data, create reader and run test
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "clickhouse"}, sqlmock.QueryMatcherRegexp)
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{Provider: "datastore"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Configure mock responses
|
||||
for _, response := range tc.queryResponses {
|
||||
@@ -2205,7 +2205,7 @@ func Test_querier_Logs_runWindowBasedListQueryAsc(t *testing.T) {
|
||||
}
|
||||
|
||||
// Create reader and querier
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore),
|
||||
|
||||
@@ -66,8 +66,8 @@ func NewQueryInfoResult(postData *v3.QueryRangeParamsV3, version string) QueryIn
|
||||
queryInfoResult.MetricsUsed = true
|
||||
}
|
||||
}
|
||||
} else if postData.CompositeQuery.QueryType == v3.QueryTypeClickHouseSQL {
|
||||
for _, query := range postData.CompositeQuery.ClickHouseQueries {
|
||||
} else if postData.CompositeQuery.QueryType == v3.QueryTypeDatastoreSQL {
|
||||
for _, query := range postData.CompositeQuery.DatastoreQueries {
|
||||
if strings.Contains(query.Query, "observe_metrics") && len(query.Query) > 0 {
|
||||
queryInfoResult.MetricsUsed = true
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ var resourceLogOperators = map[v3.FilterOperator]string{
|
||||
v3.FilterOperatorNotILike: "NOT ILIKE",
|
||||
}
|
||||
|
||||
// buildResourceFilter builds a clickhouse filter string for resource labels
|
||||
// buildResourceFilter builds a datastore filter string for resource labels
|
||||
func buildResourceFilter(logsOp string, key string, op v3.FilterOperator, value interface{}) string {
|
||||
// for all operators except contains and like
|
||||
searchKey := fmt.Sprintf("simpleJSONExtractString(labels, '%s')", key)
|
||||
@@ -37,7 +37,7 @@ func buildResourceFilter(logsOp string, key string, op v3.FilterOperator, value
|
||||
// for contains and like it will be case insensitive
|
||||
lowerSearchKey := fmt.Sprintf("simpleJSONExtractString(lower(labels), '%s')", key)
|
||||
|
||||
chFmtVal := utils.ClickHouseFormattedValue(value)
|
||||
chFmtVal := utils.DatastoreFormattedValue(value)
|
||||
|
||||
lowerValue := strings.ToLower(fmt.Sprintf("%s", value))
|
||||
|
||||
@@ -49,12 +49,12 @@ func buildResourceFilter(logsOp string, key string, op v3.FilterOperator, value
|
||||
case v3.FilterOperatorRegex, v3.FilterOperatorNotRegex:
|
||||
return fmt.Sprintf(logsOp, searchKey, chFmtVal)
|
||||
case v3.FilterOperatorContains, v3.FilterOperatorNotContains:
|
||||
// this is required as clickhouseFormattedValue add's quotes to the string
|
||||
// this is required as datastoreFormattedValue add's quotes to the string
|
||||
// we also want to treat %, _ as literals for contains
|
||||
escapedStringValue := utils.QuoteEscapedStringForContains(lowerValue, false)
|
||||
return fmt.Sprintf("%s %s '%%%s%%'", lowerSearchKey, logsOp, escapedStringValue)
|
||||
case v3.FilterOperatorLike, v3.FilterOperatorNotLike, v3.FilterOperatorILike, v3.FilterOperatorNotILike:
|
||||
// this is required as clickhouseFormattedValue add's quotes to the string
|
||||
// this is required as datastoreFormattedValue add's quotes to the string
|
||||
escapedStringValue := utils.QuoteEscapedString(lowerValue)
|
||||
return fmt.Sprintf("%s %s '%s'", lowerSearchKey, logsOp, escapedStringValue)
|
||||
default:
|
||||
@@ -62,7 +62,7 @@ func buildResourceFilter(logsOp string, key string, op v3.FilterOperator, value
|
||||
}
|
||||
}
|
||||
|
||||
// buildIndexFilterForInOperator builds a clickhouse filter string for in operator
|
||||
// buildIndexFilterForInOperator builds a datastore filter string for in operator
|
||||
// example:= x in a,b,c = (labels like '%"x"%"a"%' or labels like '%"x":"b"%' or labels like '%"x"="c"%')
|
||||
// example:= x nin a,b,c = (labels nlike '%"x"%"a"%' AND labels nlike '%"x"="b"' AND labels nlike '%"x"="c"%')
|
||||
func buildIndexFilterForInOperator(key string, op v3.FilterOperator, value interface{}) string {
|
||||
@@ -102,7 +102,7 @@ func buildIndexFilterForInOperator(key string, op v3.FilterOperator, value inter
|
||||
return ""
|
||||
}
|
||||
|
||||
// buildResourceIndexFilter builds a clickhouse filter string for resource labels
|
||||
// buildResourceIndexFilter builds a datastore filter string for resource labels
|
||||
// example:= x like '%john%' = labels like '%x%john%'
|
||||
// we have two indexes for resource attributes one is lower and one is normal.
|
||||
// for all operators other then like/contains we will use normal index
|
||||
@@ -110,7 +110,7 @@ func buildIndexFilterForInOperator(key string, op v3.FilterOperator, value inter
|
||||
// we can use lower index for =, in etc but it's difficult to do it for !=, NIN etc
|
||||
// if as x != "ABC" we cannot predict something like "not lower(labels) like '%%x%%abc%%'". It has it be "not lower(labels) like '%%x%%ABC%%'"
|
||||
func buildResourceIndexFilter(key string, op v3.FilterOperator, value interface{}) string {
|
||||
// not using clickhouseFormattedValue as we don't wan't the quotes
|
||||
// not using datastoreFormattedValue as we don't wan't the quotes
|
||||
strVal := fmt.Sprintf("%s", value)
|
||||
fmtValEscapedForContains := utils.QuoteEscapedStringForContains(strVal, true)
|
||||
fmtValEscapedForContainsLower := strings.ToLower(fmtValEscapedForContains)
|
||||
@@ -146,7 +146,7 @@ func buildResourceIndexFilter(key string, op v3.FilterOperator, value interface{
|
||||
}
|
||||
}
|
||||
|
||||
// buildResourceFiltersFromFilterItems builds a list of clickhouse filter strings for resource labels from a FilterSet.
|
||||
// buildResourceFiltersFromFilterItems builds a list of datastore filter strings for resource labels from a FilterSet.
|
||||
// It skips any filter items that are not resource attributes and checks that the operator is supported and the data type is correct.
|
||||
func buildResourceFiltersFromFilterItems(fs *v3.FilterSet) ([]string, error) {
|
||||
var conditions []string
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/querier"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/agentConf"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/clickhouseReader"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/datastoreReader"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/cloudintegrations"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/integrations"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/logparsingpipeline"
|
||||
@@ -87,7 +87,7 @@ func NewServer(config o11y.Config, o11y *o11y.HanzoO11y) (*Server, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reader := clickhouseReader.NewReader(
|
||||
reader := datastoreReader.NewReader(
|
||||
o11y.SQLStore,
|
||||
o11y.TelemetryStore,
|
||||
o11y.Prometheus,
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/ClickHouse/clickhouse-go/v2"
|
||||
datastore "github.com/ClickHouse/clickhouse-go/v2"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/model"
|
||||
)
|
||||
|
||||
@@ -31,28 +31,28 @@ func BuildServiceMapQuery(tags []model.TagQuery) (string, []interface{}) {
|
||||
switch operator {
|
||||
case model.InOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s IN @%s", key, key)
|
||||
namedArgs = append(namedArgs, clickhouse.Named(key, value))
|
||||
namedArgs = append(namedArgs, datastore.Named(key, value))
|
||||
case model.NotInOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s NOT IN @%s", key, key)
|
||||
namedArgs = append(namedArgs, clickhouse.Named(key, value))
|
||||
namedArgs = append(namedArgs, datastore.Named(key, value))
|
||||
case model.EqualOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s = @%s", key, key)
|
||||
namedArgs = append(namedArgs, clickhouse.Named(key, value))
|
||||
namedArgs = append(namedArgs, datastore.Named(key, value))
|
||||
case model.NotEqualOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s != @%s", key, key)
|
||||
namedArgs = append(namedArgs, clickhouse.Named(key, value))
|
||||
namedArgs = append(namedArgs, datastore.Named(key, value))
|
||||
case model.ContainsOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s LIKE @%s", key, key)
|
||||
namedArgs = append(namedArgs, clickhouse.Named(key, fmt.Sprintf("%%%s%%", value)))
|
||||
namedArgs = append(namedArgs, datastore.Named(key, fmt.Sprintf("%%%s%%", value)))
|
||||
case model.NotContainsOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s NOT LIKE @%s", key, key)
|
||||
namedArgs = append(namedArgs, clickhouse.Named(key, fmt.Sprintf("%%%s%%", value)))
|
||||
namedArgs = append(namedArgs, datastore.Named(key, fmt.Sprintf("%%%s%%", value)))
|
||||
case model.StartsWithOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s LIKE @%s", key, key)
|
||||
namedArgs = append(namedArgs, clickhouse.Named(key, fmt.Sprintf("%s%%", value)))
|
||||
namedArgs = append(namedArgs, datastore.Named(key, fmt.Sprintf("%s%%", value)))
|
||||
case model.NotStartsWithOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s NOT LIKE @%s", key, key)
|
||||
namedArgs = append(namedArgs, clickhouse.Named(key, fmt.Sprintf("%s%%", value)))
|
||||
namedArgs = append(namedArgs, datastore.Named(key, fmt.Sprintf("%s%%", value)))
|
||||
case model.ExistsOperator:
|
||||
filterQuery += fmt.Sprintf(" AND %s IS NOT NULL", key)
|
||||
case model.NotExistsOperator:
|
||||
|
||||
@@ -162,7 +162,7 @@ func buildTracesFilterQuery(fs *v3.FilterSet) (string, error) {
|
||||
}
|
||||
}
|
||||
if val != nil {
|
||||
fmtVal = utils.ClickHouseFormattedValue(val)
|
||||
fmtVal = utils.DatastoreFormattedValue(val)
|
||||
}
|
||||
if operator, ok := tracesOperatorMappingV3[item.Operator]; ok {
|
||||
switch item.Operator {
|
||||
@@ -317,7 +317,7 @@ func Having(items []v3.Having) string {
|
||||
// aggregate something and filter on that aggregate
|
||||
var having []string
|
||||
for _, item := range items {
|
||||
having = append(having, fmt.Sprintf("value %s %s", item.Operator, utils.ClickHouseFormattedValue(item.Value)))
|
||||
having = append(having, fmt.Sprintf("value %s %s", item.Operator, utils.DatastoreFormattedValue(item.Value)))
|
||||
}
|
||||
return strings.Join(having, " AND ")
|
||||
}
|
||||
|
||||
@@ -34,14 +34,14 @@ var tracesOperatorMappingV3 = map[v3.FilterOperator]string{
|
||||
v3.FilterOperatorNotILike: "NOT ILIKE",
|
||||
}
|
||||
|
||||
func getClickHouseTracesColumnType(columnType v3.AttributeKeyType) string {
|
||||
func getDatastoreTracesColumnType(columnType v3.AttributeKeyType) string {
|
||||
if columnType == v3.AttributeKeyTypeResource {
|
||||
return "resources"
|
||||
}
|
||||
return "attributes"
|
||||
}
|
||||
|
||||
func getClickHouseTracesColumnDataType(columnDataType v3.AttributeKeyDataType) string {
|
||||
func getDatastoreTracesColumnDataType(columnDataType v3.AttributeKeyDataType) string {
|
||||
if columnDataType == v3.AttributeKeyDataTypeFloat64 || columnDataType == v3.AttributeKeyDataTypeInt64 {
|
||||
return "number"
|
||||
}
|
||||
@@ -71,8 +71,8 @@ func getColumnName(key v3.AttributeKey, replaceAlias bool) string {
|
||||
}
|
||||
|
||||
if !key.IsColumn {
|
||||
keyType := getClickHouseTracesColumnType(key.Type)
|
||||
keyDType := getClickHouseTracesColumnDataType(key.DataType)
|
||||
keyType := getDatastoreTracesColumnType(key.Type)
|
||||
keyDType := getDatastoreTracesColumnDataType(key.DataType)
|
||||
return fmt.Sprintf("%s_%s['%s']", keyType, keyDType, key.Key)
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func BuildTracesFilterQuery(fs *v3.FilterSet, skipAllowed bool) (string, error)
|
||||
}
|
||||
}
|
||||
if val != nil {
|
||||
fmtVal = utils.ClickHouseFormattedValue(val)
|
||||
fmtVal = utils.DatastoreFormattedValue(val)
|
||||
}
|
||||
if operator, ok := tracesOperatorMappingV3[item.Operator]; ok {
|
||||
switch item.Operator {
|
||||
@@ -145,8 +145,8 @@ func BuildTracesFilterQuery(fs *v3.FilterSet, skipAllowed bool) (string, error)
|
||||
}
|
||||
conditions = append(conditions, subQuery)
|
||||
} else {
|
||||
cType := getClickHouseTracesColumnType(item.Key.Type)
|
||||
cDataType := getClickHouseTracesColumnDataType(item.Key.DataType)
|
||||
cType := getDatastoreTracesColumnType(item.Key.Type)
|
||||
cDataType := getDatastoreTracesColumnDataType(item.Key.DataType)
|
||||
col := fmt.Sprintf("%s_%s", cType, cDataType)
|
||||
conditions = append(conditions, fmt.Sprintf(operator, col, item.Key.Key))
|
||||
}
|
||||
@@ -441,8 +441,8 @@ func buildTracesQuery(start, end, step int64, mq *v3.BuilderQuery, panelType v3.
|
||||
filterSubQuery = fmt.Sprintf("%s AND %s", filterSubQuery, subQuery)
|
||||
}
|
||||
} else {
|
||||
cType := getClickHouseTracesColumnType(mq.AggregateAttribute.Type)
|
||||
cDataType := getClickHouseTracesColumnDataType(mq.AggregateAttribute.DataType)
|
||||
cType := getDatastoreTracesColumnType(mq.AggregateAttribute.Type)
|
||||
cDataType := getDatastoreTracesColumnDataType(mq.AggregateAttribute.DataType)
|
||||
filterSubQuery = fmt.Sprintf("%s AND mapContains(%s_%s, '%s')", filterSubQuery, cType, cDataType, mq.AggregateAttribute.Key)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
|
||||
)
|
||||
|
||||
func Test_getClickHouseTracesColumnType(t *testing.T) {
|
||||
func Test_getDatastoreTracesColumnType(t *testing.T) {
|
||||
type args struct {
|
||||
columnType v3.AttributeKeyType
|
||||
}
|
||||
@@ -33,14 +33,14 @@ func Test_getClickHouseTracesColumnType(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := getClickHouseTracesColumnType(tt.args.columnType); got != tt.want {
|
||||
if got := getDatastoreTracesColumnType(tt.args.columnType); got != tt.want {
|
||||
t.Errorf("GetClickhouseTracesColumnType() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_getClickHouseTracesColumnDataType(t *testing.T) {
|
||||
func Test_getDatastoreTracesColumnDataType(t *testing.T) {
|
||||
type args struct {
|
||||
columnDataType v3.AttributeKeyDataType
|
||||
}
|
||||
@@ -80,7 +80,7 @@ func Test_getClickHouseTracesColumnDataType(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := getClickHouseTracesColumnDataType(tt.args.columnDataType); got != tt.want {
|
||||
if got := getDatastoreTracesColumnDataType(tt.args.columnDataType); got != tt.want {
|
||||
t.Errorf("getClickhouseTracesColumnDataType() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -31,7 +31,7 @@ const OrderBySpanCount = "span_count"
|
||||
// Deprecated: Use the new emailing service instead
|
||||
var InviteEmailTemplate = GetOrDefaultEnv("INVITE_EMAIL_TEMPLATE", "/root/templates/invitation_email.gotmpl")
|
||||
|
||||
var MetricsExplorerClickhouseThreads = GetOrDefaultEnvInt("METRICS_EXPLORER_CLICKHOUSE_THREADS", 8)
|
||||
var MetricsExplorerClickhouseThreads = GetOrDefaultEnvInt("METRICS_EXPLORER_DATASTORE_THREADS", 8)
|
||||
var UpdatedMetricsMetadataCachePrefix = GetOrDefaultEnv("METRICS_UPDATED_METADATA_CACHE_KEY", "UPDATED_METRICS_METADATA")
|
||||
|
||||
const NormalizedMetricsMapCacheKey = "NORMALIZED_METRICS_MAP_CACHE_KEY"
|
||||
@@ -232,7 +232,7 @@ const (
|
||||
)
|
||||
|
||||
// ReservedColumnTargetAliases identifies result value from a user
|
||||
// written clickhouse query. The column alias indcate which value is
|
||||
// written datastore query. The column alias indcate which value is
|
||||
// to be considered as final result (or target)
|
||||
var ReservedColumnTargetAliases = map[string]struct{}{
|
||||
"__result": {},
|
||||
|
||||
@@ -28,7 +28,7 @@ type Reader interface {
|
||||
GetCustomRetentionTTL(ctx context.Context, orgID string) (*model.GetCustomRetentionTTLResponse, error)
|
||||
|
||||
// GetDisks returns a list of disks configured in the underlying DB. It is supported by
|
||||
// clickhouse only.
|
||||
// datastore only.
|
||||
GetDisks(ctx context.Context) (*[]model.DiskItem, *model.ApiError)
|
||||
GetTraceAggregateAttributes(ctx context.Context, req *v3.AggregateAttributeRequest) (*v3.AggregateAttributeResponse, error)
|
||||
GetTraceAttributeKeys(ctx context.Context, req *v3.FilterAttributeKeyRequest) (*v3.FilterAttributeKeyResponse, error)
|
||||
@@ -78,7 +78,7 @@ type Reader interface {
|
||||
) (*v3.QBFilterSuggestionsResponse, *model.ApiError)
|
||||
|
||||
QueryDashboardVars(ctx context.Context, query string) (*model.DashboardVar, error)
|
||||
CheckClickHouse(ctx context.Context) error
|
||||
CheckDatastore(ctx context.Context) error
|
||||
|
||||
GetMetricMetadata(context.Context, valuer.UUID, string, string) (*v3.MetricMetadataResponse, error)
|
||||
|
||||
|
||||
@@ -192,7 +192,7 @@ type QueryDataDashboard struct {
|
||||
Loading bool `json:"loading"`
|
||||
}
|
||||
|
||||
type ClickHouseQueryDashboard struct {
|
||||
type DatastoreQueryDashboard struct {
|
||||
Legend string `json:"legend"`
|
||||
Name string `json:"name"`
|
||||
Query string `json:"rawQuery"`
|
||||
@@ -223,7 +223,7 @@ type PromQueryDashboard struct {
|
||||
}
|
||||
|
||||
type Query struct {
|
||||
ClickHouse []ClickHouseQueryDashboard `json:"clickHouse"`
|
||||
Datastore []DatastoreQueryDashboard `json:"clickHouse"`
|
||||
PromQL []PromQueryDashboard `json:"promQL"`
|
||||
MetricsBuilder MetricsBuilder `json:"metricsBuilder"`
|
||||
QueryType int `json:"queryType"`
|
||||
|
||||
@@ -673,7 +673,7 @@ type AlertsInfo struct {
|
||||
EmailChannels int `json:"emailChannels"`
|
||||
MSTeamsChannels int `json:"microsoftTeamsChannels"`
|
||||
MetricsBuilderQueries int `json:"metricsBuilderQueries"`
|
||||
MetricsClickHouseQueries int `json:"metricsClickHouseQueries"`
|
||||
MetricsDatastoreQueries int `json:"metricsDatastoreQueries"`
|
||||
MetricsPrometheusQueries int `json:"metricsPrometheusQueries"`
|
||||
SpanMetricsPrometheusQueries int `json:"spanMetricsPrometheusQueries"`
|
||||
AlertNames []string `json:"alertNames"`
|
||||
|
||||
@@ -184,13 +184,13 @@ type QueryType string
|
||||
const (
|
||||
QueryTypeUnknown QueryType = "unknown"
|
||||
QueryTypeBuilder QueryType = "builder"
|
||||
QueryTypeClickHouseSQL QueryType = "clickhouse_sql"
|
||||
QueryTypeDatastoreSQL QueryType = "datastore_sql"
|
||||
QueryTypePromQL QueryType = "promql"
|
||||
)
|
||||
|
||||
func (q QueryType) Validate() error {
|
||||
switch q {
|
||||
case QueryTypeBuilder, QueryTypeClickHouseSQL, QueryTypePromQL:
|
||||
case QueryTypeBuilder, QueryTypeDatastoreSQL, QueryTypePromQL:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid query type: %s", q)
|
||||
@@ -481,23 +481,23 @@ func (p *PromQuery) Validate() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type ClickHouseQuery struct {
|
||||
type DatastoreQuery struct {
|
||||
Query string `json:"query"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Legend string `json:"legend,omitempty"`
|
||||
}
|
||||
|
||||
func (c *ClickHouseQuery) Clone() *ClickHouseQuery {
|
||||
func (c *DatastoreQuery) Clone() *DatastoreQuery {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return &ClickHouseQuery{
|
||||
return &DatastoreQuery{
|
||||
Query: c.Query,
|
||||
Disabled: c.Disabled,
|
||||
Legend: c.Legend,
|
||||
}
|
||||
}
|
||||
func (c *ClickHouseQuery) Validate() error {
|
||||
func (c *DatastoreQuery) Validate() error {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -511,7 +511,7 @@ func (c *ClickHouseQuery) Validate() error {
|
||||
|
||||
type CompositeQuery struct {
|
||||
BuilderQueries map[string]*BuilderQuery `json:"builderQueries,omitempty"`
|
||||
ClickHouseQueries map[string]*ClickHouseQuery `json:"chQueries,omitempty"`
|
||||
DatastoreQueries map[string]*DatastoreQuery `json:"chQueries,omitempty"`
|
||||
PromQueries map[string]*PromQuery `json:"promQueries,omitempty"`
|
||||
|
||||
Queries []qbtypes.QueryEnvelope `json:"queries,omitempty"`
|
||||
@@ -536,10 +536,10 @@ func (c *CompositeQuery) Clone() *CompositeQuery {
|
||||
builderQueries[name] = query.Clone()
|
||||
}
|
||||
}
|
||||
var clickHouseQueries map[string]*ClickHouseQuery
|
||||
if c.ClickHouseQueries != nil {
|
||||
clickHouseQueries = make(map[string]*ClickHouseQuery)
|
||||
for name, query := range c.ClickHouseQueries {
|
||||
var clickHouseQueries map[string]*DatastoreQuery
|
||||
if c.DatastoreQueries != nil {
|
||||
clickHouseQueries = make(map[string]*DatastoreQuery)
|
||||
for name, query := range c.DatastoreQueries {
|
||||
clickHouseQueries[name] = query.Clone()
|
||||
}
|
||||
}
|
||||
@@ -552,7 +552,7 @@ func (c *CompositeQuery) Clone() *CompositeQuery {
|
||||
}
|
||||
return &CompositeQuery{
|
||||
BuilderQueries: builderQueries,
|
||||
ClickHouseQueries: clickHouseQueries,
|
||||
DatastoreQueries: clickHouseQueries,
|
||||
PromQueries: promQueries,
|
||||
PanelType: c.PanelType,
|
||||
QueryType: c.QueryType,
|
||||
@@ -571,8 +571,8 @@ func (c *CompositeQuery) EnabledQueries() int {
|
||||
count++
|
||||
}
|
||||
}
|
||||
case QueryTypeClickHouseSQL:
|
||||
for _, query := range c.ClickHouseQueries {
|
||||
case QueryTypeDatastoreSQL:
|
||||
for _, query := range c.DatastoreQueries {
|
||||
if !query.Disabled {
|
||||
count++
|
||||
}
|
||||
@@ -604,7 +604,7 @@ func (c *CompositeQuery) Validate() error {
|
||||
return fmt.Errorf("composite query is required")
|
||||
}
|
||||
|
||||
if c.BuilderQueries == nil && c.ClickHouseQueries == nil && c.PromQueries == nil && len(c.Queries) == 0 {
|
||||
if c.BuilderQueries == nil && c.DatastoreQueries == nil && c.PromQueries == nil && len(c.Queries) == 0 {
|
||||
return fmt.Errorf("composite query must contain at least one query type")
|
||||
}
|
||||
|
||||
@@ -616,10 +616,10 @@ func (c *CompositeQuery) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
if c.QueryType == QueryTypeClickHouseSQL {
|
||||
for name, query := range c.ClickHouseQueries {
|
||||
if c.QueryType == QueryTypeDatastoreSQL {
|
||||
for name, query := range c.DatastoreQueries {
|
||||
if err := query.Validate(); err != nil {
|
||||
return fmt.Errorf("clickhouse query %s is invalid: %w", name, err)
|
||||
return fmt.Errorf("datastore query %s is invalid: %w", name, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ func fillGap(series *v3.Series, start, end, step, shiftBy int64) *v3.Series {
|
||||
return newSeries
|
||||
}
|
||||
|
||||
// TODO(srikanthccv): can WITH FILL be perfect substitute for all cases https://clickhouse.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier
|
||||
// TODO(srikanthccv): can WITH FILL be perfect substitute for all cases https://datastore.com/docs/en/sql-reference/statements/select/order-by#order-by-expr-with-fill-modifier
|
||||
func FillGaps(results []*v3.Result, params *v3.QueryRangeParamsV3) {
|
||||
if params.CompositeQuery.PanelType != v3.PanelTypeGraph {
|
||||
return
|
||||
|
||||
@@ -10,16 +10,16 @@ import (
|
||||
// postProcessResult applies having clause, metric limit, reduce function to the result
|
||||
// This function is effective for metrics data source for now, but it can be extended to other data sources
|
||||
// if needed
|
||||
// Much of this work can be done in the ClickHouse query, but we decided to do it here because:
|
||||
// Much of this work can be done in the Datastore query, but we decided to do it here because:
|
||||
// 1. Effective use of caching
|
||||
// 2. Easier to add new functions
|
||||
func PostProcessResult(result []*v3.Result, queryRangeParams *v3.QueryRangeParamsV3) ([]*v3.Result, error) {
|
||||
// Having clause is not part of the clickhouse query, so we need to apply it here
|
||||
// Having clause is not part of the datastore query, so we need to apply it here
|
||||
// It's not included in the query because it doesn't work nicely with caching
|
||||
// With this change, if you have a query with a having clause, and then you change the having clause
|
||||
// to something else, the query will still be cached.
|
||||
ApplyHavingClause(result, queryRangeParams)
|
||||
// We apply the metric limit here because it's not part of the clickhouse query
|
||||
// We apply the metric limit here because it's not part of the datastore query
|
||||
// The limit in the context of the time series query is the number of time series
|
||||
// So for the limit to work, we need to know what series to keep and what to discard
|
||||
// For us to know that, we need to execute the query first, and then apply the limit
|
||||
|
||||
@@ -208,7 +208,7 @@ func sortRows(rows []*v3.TableRow, builderQueries map[string]*v3.BuilderQuery, q
|
||||
}
|
||||
}
|
||||
|
||||
func TransformToTableForClickHouseQueries(results []*v3.Result) []*v3.Result {
|
||||
func TransformToTableForDatastoreQueries(results []*v3.Result) []*v3.Result {
|
||||
if len(results) == 0 {
|
||||
return []*v3.Result{}
|
||||
}
|
||||
|
||||
@@ -307,7 +307,7 @@ func TestSortRowsStability(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransformToTableForClickHouseQueries(t *testing.T) {
|
||||
func TestTransformToTableForDatastoreQueries(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input []*v3.Result
|
||||
@@ -547,17 +547,17 @@ func TestTransformToTableForClickHouseQueries(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := TransformToTableForClickHouseQueries(tt.input)
|
||||
result := TransformToTableForDatastoreQueries(tt.input)
|
||||
exp, _ := json.Marshal(tt.expected)
|
||||
got, _ := json.Marshal(result)
|
||||
if !bytes.Equal(got, exp) {
|
||||
t.Errorf("TransformToTableForClickHouseQueries() = %v, want %v", string(got), string(exp))
|
||||
t.Errorf("TransformToTableForDatastoreQueries() = %v, want %v", string(got), string(exp))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTransformToTableForClickHouseQueriesSorting(t *testing.T) {
|
||||
func TestTransformToTableForDatastoreQueriesSorting(t *testing.T) {
|
||||
input := []*v3.Result{
|
||||
{
|
||||
QueryName: "B",
|
||||
@@ -603,10 +603,10 @@ func TestTransformToTableForClickHouseQueriesSorting(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
result := TransformToTableForClickHouseQueries(input)
|
||||
result := TransformToTableForDatastoreQueries(input)
|
||||
exp, _ := json.Marshal(expected)
|
||||
got, _ := json.Marshal(result)
|
||||
if !bytes.Equal(got, exp) {
|
||||
t.Errorf("TransformToTableForClickHouseQueries() sorting test failed. Got %v, want %v", string(got), string(exp))
|
||||
t.Errorf("TransformToTableForDatastoreQueries() sorting test failed. Got %v, want %v", string(got), string(exp))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus/prometheustest"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/clickhouseReader"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/datastoreReader"
|
||||
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/utils/labels"
|
||||
"github.com/hanzoai/o11y/pkg/queryparser"
|
||||
@@ -96,7 +96,7 @@ func createFirstSeenMap(metricName string, groupByFields []string, evalTime time
|
||||
|
||||
// mergeFirstSeenMaps merges multiple first_seen maps into one
|
||||
// When the same key exists in multiple maps, it keeps the lowest value
|
||||
// which simulatest the behavior of the ClickHouse query
|
||||
// which simulatest the behavior of the Datastore query
|
||||
// finding the minimum first_seen timestamp across all groupBy attributes for a single series
|
||||
func mergeFirstSeenMaps(maps ...map[telemetrytypes.MetricMetadataLookupKey]int64) map[telemetrytypes.MetricMetadataLookupKey]int64 {
|
||||
result := make(map[telemetrytypes.MetricMetadataLookupKey]int64)
|
||||
@@ -240,13 +240,13 @@ func TestBaseRule_FilterNewSeries(t *testing.T) {
|
||||
expectedFiltered: []*v3.Series{}, // all should be filtered out (new series)
|
||||
},
|
||||
{
|
||||
name: "all old series - ClickHouse query",
|
||||
name: "all old series - Datastore query",
|
||||
compositeQuery: &v3.CompositeQuery{
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
Queries: []qbtypes.QueryEnvelope{
|
||||
{
|
||||
Type: qbtypes.QueryTypeClickHouseSQL,
|
||||
Spec: qbtypes.ClickHouseQuery{
|
||||
Type: qbtypes.QueryTypeDatastoreSQL,
|
||||
Spec: qbtypes.DatastoreQuery{
|
||||
Name: "CH1",
|
||||
Query: "SELECT service_name, env FROM metrics WHERE metric_name='request_total' GROUP BY service_name, env",
|
||||
Disabled: false,
|
||||
@@ -399,13 +399,13 @@ func TestBaseRule_FilterNewSeries(t *testing.T) {
|
||||
}, // both should be included - svc-old is old, svc-no-metadata can't be decided
|
||||
},
|
||||
{
|
||||
name: "series with partial metadata - ClickHouse",
|
||||
name: "series with partial metadata - Datastore",
|
||||
compositeQuery: &v3.CompositeQuery{
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
Queries: []qbtypes.QueryEnvelope{
|
||||
{
|
||||
Type: qbtypes.QueryTypeClickHouseSQL,
|
||||
Spec: qbtypes.ClickHouseQuery{
|
||||
Type: qbtypes.QueryTypeDatastoreSQL,
|
||||
Spec: qbtypes.DatastoreQuery{
|
||||
Name: "CH1",
|
||||
Query: "SELECT service_name, env FROM metrics WHERE metric_name='request_total' GROUP BY service_name, env",
|
||||
Disabled: false,
|
||||
@@ -692,8 +692,8 @@ func TestBaseRule_FilterNewSeries(t *testing.T) {
|
||||
)
|
||||
require.NoError(t, err)
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := clickhouseReader.NewReader(
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := datastoreReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheustest.New(context.Background(), settings, prometheus.Config{}, telemetryStore),
|
||||
|
||||
@@ -1069,7 +1069,7 @@ func (m *Manager) GetAlertDetailsForMetricNames(ctx context.Context, metricNames
|
||||
}
|
||||
}
|
||||
|
||||
for _, query := range rule.RuleCondition.CompositeQuery.ClickHouseQueries {
|
||||
for _, query := range rule.RuleCondition.CompositeQuery.DatastoreQueries {
|
||||
if query.Query != "" {
|
||||
for _, metricName := range metricNames {
|
||||
if strings.Contains(query.Query, metricName) {
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
"github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cmock "github.com/srikanthccv/ClickHouse-go-mock"
|
||||
cmock "github.com/srikanthccv/Datastore-go-mock"
|
||||
)
|
||||
|
||||
func TestManager_TestNotification_SendUnmatched_ThresholdRule(t *testing.T) {
|
||||
|
||||
@@ -1,147 +0,0 @@
|
||||
//go:build signoz
|
||||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
"github.com/hanzoai/o11y/pkg/alertmanager"
|
||||
alertmanagermock "github.com/hanzoai/o11y/pkg/alertmanager/alertmanagertest"
|
||||
"github.com/hanzoai/o11y/pkg/cache"
|
||||
"github.com/hanzoai/o11y/pkg/cache/cachetest"
|
||||
"github.com/hanzoai/o11y/pkg/flagger"
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus/prometheustest"
|
||||
"github.com/hanzoai/o11y/pkg/querier"
|
||||
"github.com/hanzoai/o11y/pkg/querier/o11yquerier"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/clickhouseReader"
|
||||
"github.com/hanzoai/o11y/pkg/sqlstore"
|
||||
"github.com/hanzoai/o11y/pkg/sqlstore/sqlstoretest"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore/telemetrystoretest"
|
||||
"github.com/stretchr/testify/require"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type queryMatcherAny struct {
|
||||
}
|
||||
|
||||
func (m *queryMatcherAny) Match(x string, y string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TestManagerOptions provides options for customizing the test manager creation.
|
||||
type TestManagerOptions struct {
|
||||
// AlertmanagerHook is a function that will be called with the Alertmanager mock
|
||||
// after it's created but before it's used. This allows customizing the mock behavior.
|
||||
AlertmanagerHook func(alertmanager.Alertmanager)
|
||||
|
||||
// SqlStoreHook is a function that will be called with the SQLStore mock
|
||||
// after it's created but before it's used. This allows customizing the mock behavior.
|
||||
SqlStoreHook func(sqlstore.SQLStore)
|
||||
|
||||
// TelemetryStoreHook is a function that will be called with the TelemetryStore mock
|
||||
// after it's created but before it's used. This allows customizing the mock behavior.
|
||||
TelemetryStoreHook func(telemetrystore.TelemetryStore)
|
||||
|
||||
// ManagerOptionsHook is a function that will be called with the ManagerOptions
|
||||
// before the manager is created. This allows customizing the manager options (e.g., setting Prometheus).
|
||||
ManagerOptionsHook func(*ManagerOptions)
|
||||
}
|
||||
|
||||
// NewTestManager creates a Manager instance for testing purposes.
|
||||
// It sets up all the necessary mocks and dependencies required for testing.
|
||||
// Options can be provided to customize the manager behavior. If nil, default options are used.
|
||||
func NewTestManager(t *testing.T, testOpts *TestManagerOptions) *Manager {
|
||||
// mocking the alertmanager + capturing the triggered test alerts
|
||||
fAlert := alertmanagermock.NewMockAlertmanager(t)
|
||||
|
||||
// Call the Alertmanager hook if provided
|
||||
if testOpts != nil && testOpts.AlertmanagerHook != nil {
|
||||
testOpts.AlertmanagerHook(fAlert)
|
||||
}
|
||||
|
||||
cacheObj, err := cachetest.New(cache.Config{
|
||||
Provider: "memory",
|
||||
Memory: cache.Memory{
|
||||
NumCounters: 1000,
|
||||
MaxCost: 1 << 20,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create SQLStore mock
|
||||
sqlStore := sqlstoretest.New(sqlstore.Config{Provider: "sqlite"}, sqlmock.QueryMatcherRegexp)
|
||||
|
||||
// Call the SqlStore hook if provided
|
||||
if testOpts != nil && testOpts.SqlStoreHook != nil {
|
||||
testOpts.SqlStoreHook(sqlStore)
|
||||
}
|
||||
|
||||
// Create TelemetryStore mock
|
||||
telemetryStore := telemetrystoretest.New(telemetrystore.Config{}, &queryMatcherAny{})
|
||||
|
||||
// Call the TelemetryStore hook if provided
|
||||
if testOpts != nil && testOpts.TelemetryStoreHook != nil {
|
||||
testOpts.TelemetryStoreHook(telemetryStore)
|
||||
}
|
||||
|
||||
// Create reader with mocked telemetry store
|
||||
readerCache, err := cachetest.New(cache.Config{
|
||||
Provider: "memory",
|
||||
Memory: cache.Memory{
|
||||
NumCounters: 10 * 1000,
|
||||
MaxCost: 1 << 26,
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
providerSettings := instrumentationtest.New().ToProviderSettings()
|
||||
prometheus := prometheustest.New(context.Background(), providerSettings, prometheus.Config{}, telemetryStore)
|
||||
reader := clickhouseReader.NewReader(
|
||||
nil,
|
||||
telemetryStore,
|
||||
prometheus,
|
||||
"",
|
||||
time.Duration(time.Second),
|
||||
nil,
|
||||
readerCache,
|
||||
options,
|
||||
)
|
||||
|
||||
flagger, err := flagger.New(context.Background(), instrumentationtest.New().ToProviderSettings(), flagger.Config{}, flagger.MustNewRegistry())
|
||||
if err != nil {
|
||||
t.Fatalf("failed to create flagger: %v", err)
|
||||
}
|
||||
|
||||
// Create mock querierV5 with test values
|
||||
providerFactory := o11yquerier.NewFactory(telemetryStore, prometheus, readerCache, flagger)
|
||||
mockQuerier, err := providerFactory.New(context.Background(), providerSettings, querier.Config{})
|
||||
require.NoError(t, err)
|
||||
|
||||
mgrOpts := &ManagerOptions{
|
||||
Logger: zap.NewNop(),
|
||||
SLogger: instrumentationtest.New().Logger(),
|
||||
Cache: cacheObj,
|
||||
Alertmanager: fAlert,
|
||||
Querier: mockQuerier,
|
||||
TelemetryStore: telemetryStore,
|
||||
Reader: reader,
|
||||
SqlStore: sqlStore, // SQLStore needed for SendAlerts to query organizations
|
||||
}
|
||||
|
||||
// Call the ManagerOptions hook if provided to allow customization
|
||||
if testOpts != nil && testOpts.ManagerOptionsHook != nil {
|
||||
testOpts.ManagerOptionsHook(mgrOpts)
|
||||
}
|
||||
|
||||
mgr, err := NewManager(mgrOpts)
|
||||
require.NoError(t, err)
|
||||
|
||||
return mgr
|
||||
}
|
||||
@@ -10,12 +10,12 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
pql "github.com/prometheus/prometheus/promql"
|
||||
cmock "github.com/srikanthccv/ClickHouse-go-mock"
|
||||
cmock "github.com/srikanthccv/Datastore-go-mock"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus/prometheustest"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/clickhouseReader"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/datastoreReader"
|
||||
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
|
||||
qslabels "github.com/hanzoai/o11y/pkg/query-service/utils/labels"
|
||||
"github.com/hanzoai/o11y/pkg/telemetrystore"
|
||||
@@ -963,8 +963,8 @@ func TestPromRuleUnitCombinations(t *testing.T) {
|
||||
"summary": "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}",
|
||||
}
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
rule, err := NewPromRule("69", valuer.GenerateUUID(), &postableRule, logger, reader, promProvider)
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
@@ -1079,8 +1079,8 @@ func _Enable_this_after_9146_issue_fix_is_merged_TestPromRuleNoData(t *testing.T
|
||||
"summary": "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}",
|
||||
}
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
rule, err := NewPromRule("69", valuer.GenerateUUID(), &postableRule, logger, reader, promProvider)
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
@@ -1311,8 +1311,8 @@ func TestMultipleThresholdPromRule(t *testing.T) {
|
||||
"summary": "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}",
|
||||
}
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
rule, err := NewPromRule("69", valuer.GenerateUUID(), &postableRule, logger, reader, promProvider)
|
||||
if err != nil {
|
||||
assert.NoError(t, err)
|
||||
@@ -1447,8 +1447,8 @@ func TestPromRule_NoData(t *testing.T) {
|
||||
_ = promProvider.Close()
|
||||
}()
|
||||
|
||||
options := clickhouseReader.NewOptions("primaryNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
options := datastoreReader.NewOptions("primaryNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
rule, err := NewPromRule("some-id", valuer.GenerateUUID(), &postableRule, logger, reader, promProvider)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1596,8 +1596,8 @@ func TestPromRule_NoData_AbsentFor(t *testing.T) {
|
||||
_ = promProvider.Close()
|
||||
}()
|
||||
|
||||
options := clickhouseReader.NewOptions("primaryNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
options := datastoreReader.NewOptions("primaryNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
rule, err := NewPromRule("some-id", valuer.GenerateUUID(), &postableRule, logger, reader, promProvider)
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -1754,8 +1754,8 @@ func TestPromRuleEval_RequireMinPoints(t *testing.T) {
|
||||
_ = promProvider.Close()
|
||||
}()
|
||||
|
||||
options := clickhouseReader.NewOptions("primaryNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
options := datastoreReader.NewOptions("primaryNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, promProvider, "", time.Second, nil, nil, options)
|
||||
rule, err := NewPromRule("some-id", valuer.GenerateUUID(), &postableRule, logger, reader, promProvider)
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -130,7 +130,7 @@ func (r *ThresholdRule) prepareQueryRange(ctx context.Context, ts time.Time) (*v
|
||||
startTs, endTs := r.Timestamps(ts)
|
||||
start, end := startTs.UnixMilli(), endTs.UnixMilli()
|
||||
|
||||
if r.ruleCondition.QueryType() == v3.QueryTypeClickHouseSQL {
|
||||
if r.ruleCondition.QueryType() == v3.QueryTypeDatastoreSQL {
|
||||
params := &v3.QueryRangeParamsV3{
|
||||
Start: start,
|
||||
End: end,
|
||||
@@ -139,7 +139,7 @@ func (r *ThresholdRule) prepareQueryRange(ctx context.Context, ts time.Time) (*v
|
||||
QueryType: r.ruleCondition.CompositeQuery.QueryType,
|
||||
PanelType: r.ruleCondition.CompositeQuery.PanelType,
|
||||
BuilderQueries: make(map[string]*v3.BuilderQuery),
|
||||
ClickHouseQueries: make(map[string]*v3.ClickHouseQuery),
|
||||
DatastoreQueries: make(map[string]*v3.DatastoreQuery),
|
||||
PromQueries: make(map[string]*v3.PromQuery),
|
||||
Unit: r.ruleCondition.CompositeQuery.Unit,
|
||||
},
|
||||
@@ -147,11 +147,11 @@ func (r *ThresholdRule) prepareQueryRange(ctx context.Context, ts time.Time) (*v
|
||||
NoCache: true,
|
||||
}
|
||||
querytemplate.AssignReservedVarsV3(params)
|
||||
for name, chQuery := range r.ruleCondition.CompositeQuery.ClickHouseQueries {
|
||||
for name, chQuery := range r.ruleCondition.CompositeQuery.DatastoreQueries {
|
||||
if chQuery.Disabled {
|
||||
continue
|
||||
}
|
||||
tmpl := template.New("clickhouse-query")
|
||||
tmpl := template.New("datastore-query")
|
||||
tmpl, err := tmpl.Parse(chQuery.Query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -161,7 +161,7 @@ func (r *ThresholdRule) prepareQueryRange(ctx context.Context, ts time.Time) (*v
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params.CompositeQuery.ClickHouseQueries[name] = &v3.ClickHouseQuery{
|
||||
params.CompositeQuery.DatastoreQueries[name] = &v3.DatastoreQuery{
|
||||
Query: query.String(),
|
||||
Disabled: chQuery.Disabled,
|
||||
Legend: chQuery.Legend,
|
||||
|
||||
@@ -11,14 +11,14 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
cmock "github.com/srikanthccv/ClickHouse-go-mock"
|
||||
cmock "github.com/srikanthccv/Datastore-go-mock"
|
||||
|
||||
"github.com/hanzoai/o11y/pkg/cache"
|
||||
"github.com/hanzoai/o11y/pkg/cache/cachetest"
|
||||
"github.com/hanzoai/o11y/pkg/instrumentation/instrumentationtest"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus"
|
||||
"github.com/hanzoai/o11y/pkg/prometheus/prometheustest"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/clickhouseReader"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/app/datastoreReader"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/common"
|
||||
v3 "github.com/hanzoai/o11y/pkg/query-service/model/v3"
|
||||
"github.com/hanzoai/o11y/pkg/query-service/utils/labels"
|
||||
@@ -495,8 +495,8 @@ func TestThresholdRuleEvalDelay(t *testing.T) {
|
||||
}},
|
||||
RuleCondition: &ruletypes.RuleCondition{
|
||||
CompositeQuery: &v3.CompositeQuery{
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
DatastoreQueries: map[string]*v3.DatastoreQuery{
|
||||
"A": {
|
||||
Query: "SELECT 1 >= {{.start_timestamp_ms}} AND 1 <= {{.end_timestamp_ms}}",
|
||||
},
|
||||
@@ -539,15 +539,15 @@ func TestThresholdRuleEvalDelay(t *testing.T) {
|
||||
|
||||
params, err := rule.prepareQueryRange(context.Background(), ts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.expectedQuery, params.CompositeQuery.ClickHouseQueries["A"].Query, "Test case %d", idx)
|
||||
assert.Equal(t, c.expectedQuery, params.CompositeQuery.DatastoreQueries["A"].Query, "Test case %d", idx)
|
||||
|
||||
secondTimeParams, err := rule.prepareQueryRange(context.Background(), ts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.expectedQuery, secondTimeParams.CompositeQuery.ClickHouseQueries["A"].Query, "Test case %d", idx)
|
||||
assert.Equal(t, c.expectedQuery, secondTimeParams.CompositeQuery.DatastoreQueries["A"].Query, "Test case %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
func TestThresholdRuleClickHouseTmpl(t *testing.T) {
|
||||
func TestThresholdRuleDatastoreTmpl(t *testing.T) {
|
||||
postableRule := ruletypes.PostableRule{
|
||||
AlertName: "Tricky Condition Tests",
|
||||
AlertType: ruletypes.AlertTypeMetric,
|
||||
@@ -558,8 +558,8 @@ func TestThresholdRuleClickHouseTmpl(t *testing.T) {
|
||||
}},
|
||||
RuleCondition: &ruletypes.RuleCondition{
|
||||
CompositeQuery: &v3.CompositeQuery{
|
||||
QueryType: v3.QueryTypeClickHouseSQL,
|
||||
ClickHouseQueries: map[string]*v3.ClickHouseQuery{
|
||||
QueryType: v3.QueryTypeDatastoreSQL,
|
||||
DatastoreQueries: map[string]*v3.DatastoreQuery{
|
||||
"A": {
|
||||
Query: "SELECT 1 >= {{.start_timestamp_ms}} AND 1 <= {{.end_timestamp_ms}}",
|
||||
},
|
||||
@@ -601,11 +601,11 @@ func TestThresholdRuleClickHouseTmpl(t *testing.T) {
|
||||
|
||||
params, err := rule.prepareQueryRange(context.Background(), ts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.expectedQuery, params.CompositeQuery.ClickHouseQueries["A"].Query, "Test case %d", idx)
|
||||
assert.Equal(t, c.expectedQuery, params.CompositeQuery.DatastoreQueries["A"].Query, "Test case %d", idx)
|
||||
|
||||
secondTimeParams, err := rule.prepareQueryRange(context.Background(), ts)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, c.expectedQuery, secondTimeParams.CompositeQuery.ClickHouseQueries["A"].Query, "Test case %d", idx)
|
||||
assert.Equal(t, c.expectedQuery, secondTimeParams.CompositeQuery.DatastoreQueries["A"].Query, "Test case %d", idx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -767,7 +767,7 @@ func TestThresholdRuleUnitCombinations(t *testing.T) {
|
||||
"summary": "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}",
|
||||
}
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
readerCache, err := cachetest.New(
|
||||
cache.Config{
|
||||
Provider: "memory",
|
||||
@@ -778,7 +778,7 @@ func TestThresholdRuleUnitCombinations(t *testing.T) {
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Duration(time.Second), nil, readerCache, options)
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Duration(time.Second), nil, readerCache, options)
|
||||
rule, err := NewThresholdRule("69", valuer.GenerateUUID(), &postableRule, reader, nil, logger)
|
||||
rule.TemporalityMap = map[string]map[v3.Temporality]bool{
|
||||
"observe_calls_total": {
|
||||
@@ -892,8 +892,8 @@ func TestThresholdRuleNoData(t *testing.T) {
|
||||
},
|
||||
)
|
||||
assert.NoError(t, err)
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Duration(time.Second), nil, readerCache, options)
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Duration(time.Second), nil, readerCache, options)
|
||||
|
||||
rule, err := NewThresholdRule("69", valuer.GenerateUUID(), &postableRule, reader, nil, logger)
|
||||
rule.TemporalityMap = map[string]map[v3.Temporality]bool{
|
||||
@@ -1012,8 +1012,8 @@ func TestThresholdRuleTracesLink(t *testing.T) {
|
||||
"summary": "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}",
|
||||
}
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Duration(time.Second), nil, nil, options)
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Duration(time.Second), nil, nil, options)
|
||||
|
||||
rule, err := NewThresholdRule("69", valuer.GenerateUUID(), &postableRule, reader, nil, logger)
|
||||
rule.TemporalityMap = map[string]map[v3.Temporality]bool{
|
||||
@@ -1149,8 +1149,8 @@ func TestThresholdRuleLogsLink(t *testing.T) {
|
||||
"summary": "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}",
|
||||
}
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Duration(time.Second), nil, nil, options)
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Duration(time.Second), nil, nil, options)
|
||||
|
||||
rule, err := NewThresholdRule("69", valuer.GenerateUUID(), &postableRule, reader, nil, logger)
|
||||
rule.TemporalityMap = map[string]map[v3.Temporality]bool{
|
||||
@@ -1406,7 +1406,7 @@ func TestMultipleThresholdRule(t *testing.T) {
|
||||
"summary": "The rule threshold is set to {{$threshold}}, and the observed metric value is {{$value}}",
|
||||
}
|
||||
|
||||
options := clickhouseReader.NewOptions("", "", "archiveNamespace")
|
||||
options := datastoreReader.NewOptions("", "", "archiveNamespace")
|
||||
readerCache, err := cachetest.New(
|
||||
cache.Config{
|
||||
Provider: "memory",
|
||||
@@ -1417,7 +1417,7 @@ func TestMultipleThresholdRule(t *testing.T) {
|
||||
},
|
||||
)
|
||||
require.NoError(t, err)
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Second, nil, readerCache, options)
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore), "", time.Second, nil, readerCache, options)
|
||||
rule, err := NewThresholdRule("69", valuer.GenerateUUID(), &postableRule, reader, nil, logger)
|
||||
rule.TemporalityMap = map[string]map[v3.Temporality]bool{
|
||||
"observe_calls_total": {
|
||||
@@ -2207,7 +2207,7 @@ func TestThresholdEval_RequireMinPoints(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
options := clickhouseReader.NewOptions("primaryNamespace")
|
||||
options := datastoreReader.NewOptions("primaryNamespace")
|
||||
readerCache, err := cachetest.New(
|
||||
cache.Config{
|
||||
Provider: "memory",
|
||||
@@ -2220,7 +2220,7 @@ func TestThresholdEval_RequireMinPoints(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
prometheusProvider := prometheustest.New(context.Background(), instrumentationtest.New().ToProviderSettings(), prometheus.Config{}, telemetryStore)
|
||||
reader := clickhouseReader.NewReader(nil, telemetryStore, prometheusProvider, "", time.Second, nil, readerCache, options)
|
||||
reader := datastoreReader.NewReader(nil, telemetryStore, prometheusProvider, "", time.Second, nil, readerCache, options)
|
||||
|
||||
rule, err := NewThresholdRule("some-id", valuer.GenerateUUID(), &postableRule, reader, nil, logger)
|
||||
t.Run(fmt.Sprintf("%d Version=%s, %s", idx, version, c.description), func(t *testing.T) {
|
||||
|
||||
@@ -28,7 +28,7 @@ func BuildFilterConditions(fs *v3.FilterSet, skipKey string) ([]string, error) {
|
||||
if op == v3.FilterOperatorContains || op == v3.FilterOperatorNotContains {
|
||||
toFormat = fmt.Sprintf("%%%s%%", toFormat)
|
||||
}
|
||||
fmtVal := ClickHouseFormattedValue(toFormat)
|
||||
fmtVal := DatastoreFormattedValue(toFormat)
|
||||
|
||||
// Determine if the key is a JSON key or a normal column
|
||||
isJSONKey := false
|
||||
|
||||
@@ -153,17 +153,17 @@ func ValidateAndCastValue(v interface{}, dataType v3.AttributeKeyDataType) (inte
|
||||
}
|
||||
|
||||
func QuoteEscapedString(str string) string {
|
||||
// https://clickhouse.com/docs/en/sql-reference/syntax#string
|
||||
// https://datastore.com/docs/en/sql-reference/syntax#string
|
||||
str = strings.ReplaceAll(str, `\`, `\\`)
|
||||
str = strings.ReplaceAll(str, `'`, `\'`)
|
||||
return str
|
||||
}
|
||||
|
||||
func QuoteEscapedStringForContains(str string, isIndex bool) string {
|
||||
// https: //clickhouse.com/docs/en/sql-reference/functions/string-search-functions#like
|
||||
// https: //datastore.com/docs/en/sql-reference/functions/string-search-functions#like
|
||||
str = QuoteEscapedString(str)
|
||||
|
||||
// we are adding this because if a string contains quote `"` it will be stored as \" in clickhouse
|
||||
// we are adding this because if a string contains quote `"` it will be stored as \" in datastore
|
||||
// to query that using like our query should be \\\\"
|
||||
if isIndex {
|
||||
// isIndex is true means that the extra slash is present
|
||||
@@ -176,8 +176,8 @@ func QuoteEscapedStringForContains(str string, isIndex bool) string {
|
||||
return str
|
||||
}
|
||||
|
||||
// ClickHouseFormattedValue formats the value to be used in clickhouse query
|
||||
func ClickHouseFormattedValue(v interface{}) string {
|
||||
// DatastoreFormattedValue formats the value to be used in datastore query
|
||||
func DatastoreFormattedValue(v interface{}) string {
|
||||
// if it's pointer convert it to a value
|
||||
v = getPointerValue(v)
|
||||
|
||||
@@ -231,17 +231,17 @@ func ClickHouseFormattedValue(v interface{}) string {
|
||||
}
|
||||
}
|
||||
|
||||
func ClickHouseFormattedMetricNames(v interface{}) string {
|
||||
func DatastoreFormattedMetricNames(v interface{}) string {
|
||||
if name, ok := v.(string); ok {
|
||||
transitionedMetrics := metrics.GetTransitionedMetric(name, !constants.IsDotMetricsEnabled)
|
||||
if transitionedMetrics != name {
|
||||
return ClickHouseFormattedValue([]interface{}{transitionedMetrics})
|
||||
return DatastoreFormattedValue([]interface{}{transitionedMetrics})
|
||||
} else {
|
||||
return ClickHouseFormattedValue([]interface{}{name})
|
||||
return DatastoreFormattedValue([]interface{}{name})
|
||||
}
|
||||
}
|
||||
|
||||
return ClickHouseFormattedValue(v)
|
||||
return DatastoreFormattedValue(v)
|
||||
}
|
||||
|
||||
func AddBackTickToFormatTag(str string) string {
|
||||
|
||||
@@ -316,7 +316,7 @@ var onePointOne = 1.1
|
||||
var oneString = "1"
|
||||
var trueBool = true
|
||||
|
||||
var testClickHouseFormattedValueData = []struct {
|
||||
var testDatastoreFormattedValueData = []struct {
|
||||
name string
|
||||
value interface{}
|
||||
want interface{}
|
||||
@@ -396,12 +396,12 @@ var testClickHouseFormattedValueData = []struct {
|
||||
},
|
||||
}
|
||||
|
||||
func TestClickHouseFormattedValue(t *testing.T) {
|
||||
for _, tt := range testClickHouseFormattedValueData {
|
||||
func TestDatastoreFormattedValue(t *testing.T) {
|
||||
for _, tt := range testDatastoreFormattedValueData {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := ClickHouseFormattedValue(tt.value)
|
||||
got := DatastoreFormattedValue(tt.value)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ClickHouseFormattedValue() = %v, want %v", got, tt.want)
|
||||
t.Errorf("DatastoreFormattedValue() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -449,7 +449,7 @@ func TestGetClickhouseColumnName(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := GetClickhouseColumnName(tt.typeName, tt.dataType, tt.field)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("ClickHouseFormattedValue() = %v, want %v", got, tt.want)
|
||||
t.Errorf("DatastoreFormattedValue() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -478,7 +478,7 @@ func TestGetEpochNanoSecs(t *testing.T) {
|
||||
t.Run(tt.Name, func(t *testing.T) {
|
||||
got := GetEpochNanoSecs(tt.Epoch)
|
||||
if !reflect.DeepEqual(got, tt.Result) {
|
||||
t.Errorf("ClickHouseFormattedValue() = %v, want %v", got, tt.Result)
|
||||
t.Errorf("DatastoreFormattedValue() = %v, want %v", got, tt.Result)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user