// analyzeWithCanaryBaseline returns false if canary deviates in the specified direction compared to baseline.
// Return an error if the evaluation could not be executed normally.
analyzeWithCanaryBaseline returns false if canary deviates in the specified direction compared to baseline.
Return an error if the evaluation could not be executed normally.
// analyzeWithCanaryBaseline returns false if canary deviates in the specified direction compared to baseline . // Return an error if the evaluation could not be executed normally .
func (a *metricsAnalyzer) analyzeWithCanaryBaseline(ctx context.Context) (bool, error) {
	now := time.Now()
	queryRange := metrics.QueryRange{
		From: now.Add(-a.cfg.Interval.Duration()),
		To:   now,
	}
	canaryQuery, err := a.renderQuery(a.cfg.Query, a.cfg.CanaryArgs, canaryVariantName)
	if err != nil {
		return false, fmt.Errorf("failed to render query template for Canary: %w", err)
	}
	baselineQuery, err := a.renderQuery(a.cfg.Query, a.cfg.BaselineArgs, baselineVariantName)
	if err != nil {
		return false, fmt.Errorf("failed to render query template for Baseline: %w", err)
	}
	canaryPoints, err := a.provider.QueryPoints(ctx, canaryQuery, queryRange)
	if err != nil {
		return false, fmt.Errorf("failed to run query to fetch metrics for the Canary variant: %w: query range: %s: performed query: %q", err, &queryRange, canaryQuery)
	}
	canaryPointsCount := len(canaryPoints)
	a.logPersister.Infof("[%s] Got %d data points for Canary from the query: %q", a.id, canaryPointsCount, canaryQuery)
	canaryValues := make([]float64, 0, canaryPointsCount)
	for i := range canaryPoints {
		canaryValues = append(canaryValues, canaryPoints[i].Value)
	}
	baselinePoints, err := a.provider.QueryPoints(ctx, baselineQuery, queryRange)
	if err != nil {
		return false, fmt.Errorf("failed to run query to fetch metrics for the Baseline variant: %w: query range: %s: performed query: %q", err, &queryRange, baselineQuery)
	}
	baselinePointsCount := len(baselinePoints)
	a.logPersister.Infof("[%s] Got %d data points for Baseline from the query: %q", a.id, baselinePointsCount, baselineQuery)
	baselineValues := make([]float64, 0, baselinePointsCount)
	for i := range baselinePoints {
		baselineValues = append(baselineValues, baselinePoints[i].Value)
	}
	expected, err := compare(canaryValues, baselineValues, a.cfg.Deviation)
	if err != nil {
		a.logPersister.Errorf("[%s] Failed to compare data points: %v", a.id, err)
		a.logPersister.Infof("[%s] Performed query for Canary: %q", a.id, canaryQuery)
		a.logPersister.Infof("[%s] Performed query for Baseline: %q", a.id, baselineQuery)
		return false, err
	}
	if !expected {
		a.logPersister.Errorf("[%s] The difference between Canary and Baseline is statistically significant", a.id)
		a.logPersister.Infof("[%s] Performed query range: %q", a.id, &queryRange)
		a.logPersister.Infof("[%s] Performed query for Canary: %q", a.id, canaryQuery)
		a.logPersister.Infof("[%s] Performed query for Baseline: %q", a.id, baselineQuery)
		a.logPersister.Infof("[%s] Canary data points acquired:", a.id)
		for i := range canaryPoints {
			a.logPersister.Infof("[%s] %s", a.id, &canaryPoints[i])
		}
		a.logPersister.Infof("[%s] Baseline data points acquired:", a.id)
		for i := range baselinePoints {
			a.logPersister.Infof("[%s] %s", a.id, &baselinePoints[i])
		}
		return false, nil
	}
	return true, nil
}
func ( a * metricsAnalyzer ) analyzeWithCanaryBaseline ( ctx context . Context ) ( bool , error ) { now := time . Now ( ) queryRange := metrics . QueryRange { From : now . Add ( - a . cfg . Interval . Duration ( ) ) , To : now , } canaryQuery , err := a . renderQuery ( a . cfg . Query , a . cfg . CanaryArgs , canaryVariantName ) if err != nil { return false , fmt . Errorf ( " " , err ) } baselineQuery , err := a . renderQuery ( a . cfg . Query , a . cfg . BaselineArgs , baselineVariantName ) if err != nil { return false , fmt . Errorf ( " " , err ) } canaryPoints , err := a . provider . QueryPoints ( ctx , canaryQuery , queryRange ) if err != nil { return false , fmt . Errorf ( " " , err , & queryRange , canaryQuery ) } canaryPointsCount := len ( canaryPoints ) a . logPersister . Infof ( " " , a . id , canaryPointsCount , canaryQuery ) canaryValues := make ( [ ] float64 , 0 , canaryPointsCount ) for i := range canaryPoints { canaryValues = append ( canaryValues , canaryPoints [ i ] . Value ) } baselinePoints , err := a . provider . QueryPoints ( ctx , baselineQuery , queryRange ) if err != nil { return false , fmt . Errorf ( " " , err , & queryRange , baselineQuery ) } baselinePointsCount := len ( baselinePoints ) a . logPersister . Infof ( " " , a . id , baselinePointsCount , baselineQuery ) baselineValues := make ( [ ] float64 , 0 , baselinePointsCount ) for i := range baselinePoints { baselineValues = append ( baselineValues , baselinePoints [ i ] . Value ) } expected , err := compare ( canaryValues , baselineValues , a . cfg . Deviation ) if err != nil { a . logPersister . Errorf ( " " , a . id , err ) a . logPersister . Infof ( " " , a . id , canaryQuery ) a . logPersister . Infof ( " " , a . id , baselineQuery ) return false , err } if ! expected { a . logPersister . Errorf ( " " , a . id ) a . logPersister . Infof ( " " , a . id , & queryRange ) a . logPersister . Infof ( " " , a . id , canaryQuery ) a . logPersister . Infof ( " " , a . id , baselineQuery ) a . logPersister . Infof ( " " , a . id ) for i := range canaryPoints { a . logPersister . Infof ( " " , a . id , & canaryPoints [ i ] ) } a . logPersister . Infof ( " " , a . id ) for i := range baselinePoints { a . logPersister . Infof ( " " , a . id , & baselinePoints [ i ] ) } return false , nil } return true , nil }
analyzeWithCanaryBaseline returns false if canary deviates in the specified direction compared to baseline.
analyzeWithCanaryBaseline returns false if canary deviates in the specified direction compared to baseline .
// Fetch data points from Canary. // Fetch data points from Baseline.