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:
2026-05-21 13:14:13 -07:00
parent c3f551121f
commit 6619e74e1f
147 changed files with 1036 additions and 2720 deletions
+1 -1
View File
@@ -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
},
})
+5 -5
View File
@@ -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
}
+10 -10
View File
@@ -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
}
+1 -1
View File
@@ -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)
+3 -3
View File
@@ -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"
+12 -12
View File
@@ -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, &timestampMs, &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
}
+1 -1
View File
@@ -41,5 +41,5 @@ func (c Config) Validate() error {
}
func (c Config) Provider() string {
return "clickhouse"
return "datastore"
}
+2 -2
View File
@@ -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 (
-58
View File
@@ -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
}
-108
View File
@@ -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)
}
})
}
}
+3 -3
View File
@@ -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
+6 -6
View File
@@ -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
+1 -1
View File
@@ -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"}
)
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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
+7 -7
View File
@@ -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
+2 -2
View File
@@ -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
@@ -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) {
/*
@@ -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 {
@@ -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())
@@ -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,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,
@@ -1,4 +1,4 @@
package clickhouseReader
package datastoreReader
import (
"testing"
+43 -43
View File
@@ -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
}
+2 -2
View File
@@ -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 {
+10 -10
View File
@@ -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 ")
}
+2 -2
View File
@@ -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 :=
+1 -1
View File
@@ -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")
}
+10 -10
View File
@@ -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}
+10 -10
View File
@@ -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,
+7 -7
View File
@@ -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
+11 -11
View File
@@ -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")
+13 -13
View File
@@ -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),
+5 -5
View File
@@ -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,
+12 -12
View File
@@ -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),
+2 -2
View File
@@ -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
+2 -2
View File
@@ -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,
+9 -9
View File
@@ -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)
}
})
+2 -2
View File
@@ -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": {},
+2 -2
View File
@@ -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)
+2 -2
View File
@@ -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"`
+1 -1
View File
@@ -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"`
+18 -18
View File
@@ -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)
}
}
}
+1 -1
View File
@@ -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
+3 -3
View File
@@ -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
+1 -1
View File
@@ -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{}
}
+6 -6
View File
@@ -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 -12
View File
@@ -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),
+1 -1
View File
@@ -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) {
+1 -1
View File
@@ -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
}
+14 -14
View File
@@ -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)
+5 -5
View File
@@ -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,
+23 -23
View File
@@ -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) {
+1 -1
View File
@@ -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
+9 -9
View File
@@ -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 {
+7 -7
View File
@@ -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