// analyzeWithCanaryPrimary returns false if canary deviates in the specified direction compared to primary.
// Return an error if the evaluation could not be executed normally.
analyzeWithCanaryPrimary returns false if canary deviates in the specified direction compared to primary.
Return an error if the evaluation could not be executed normally.
// analyzeWithCanaryPrimary returns false if canary deviates in the specified direction compared to primary . // Return an error if the evaluation could not be executed normally .
func (a *metricsAnalyzer) analyzeWithCanaryPrimary(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)
	}
	primaryQuery, err := a.renderQuery(a.cfg.Query, a.cfg.PrimaryArgs, primaryVariantName)
	if err != nil {
		return false, fmt.Errorf("failed to render query template for Primary: %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: performed query: %q", err, 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)
	}
	primaryPoints, err := a.provider.QueryPoints(ctx, primaryQuery, queryRange)
	if err != nil {
		return false, fmt.Errorf("failed to run query to fetch metrics for the Primary variant: %w: performed query: %q", err, primaryQuery)
	}
	primaryPointsCount := len(primaryPoints)
	a.logPersister.Infof("[%s] Got %d data points for Primary from the query: %q", a.id, primaryPointsCount, primaryQuery)
	primaryValues := make([]float64, 0, primaryPointsCount)
	for i := range primaryPoints {
		primaryValues = append(primaryValues, primaryPoints[i].Value)
	}
	expected, err := compare(canaryValues, primaryValues, 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 Primary: %q", a.id, primaryQuery)
		return false, err
	}
	if !expected {
		a.logPersister.Errorf("[%s] The difference between Canary and Primary 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 Primary: %q", a.id, primaryQuery)
		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] Primary data points acquired:", a.id)
		for i := range primaryPoints {
			a.logPersister.Infof("[%s] %s", a.id, &primaryPoints[i])
		}
		return false, nil
	}
	return true, nil
}
func ( a * metricsAnalyzer ) analyzeWithCanaryPrimary ( 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 ) } primaryQuery , err := a . renderQuery ( a . cfg . Query , a . cfg . PrimaryArgs , primaryVariantName ) if err != nil { return false , fmt . Errorf ( " " , err ) } canaryPoints , err := a . provider . QueryPoints ( ctx , canaryQuery , queryRange ) if err != nil { return false , fmt . Errorf ( " " , err , 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 ) } primaryPoints , err := a . provider . QueryPoints ( ctx , primaryQuery , queryRange ) if err != nil { return false , fmt . Errorf ( " " , err , primaryQuery ) } primaryPointsCount := len ( primaryPoints ) a . logPersister . Infof ( " " , a . id , primaryPointsCount , primaryQuery ) primaryValues := make ( [ ] float64 , 0 , primaryPointsCount ) for i := range primaryPoints { primaryValues = append ( primaryValues , primaryPoints [ i ] . Value ) } expected , err := compare ( canaryValues , primaryValues , a . cfg . Deviation ) if err != nil { a . logPersister . Errorf ( " " , a . id , err ) a . logPersister . Infof ( " " , a . id , canaryQuery ) a . logPersister . Infof ( " " , a . id , primaryQuery ) 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 , primaryQuery ) a . logPersister . Infof ( " " , a . id ) for i := range canaryPoints { a . logPersister . Infof ( " " , a . id , & canaryPoints [ i ] ) } a . logPersister . Infof ( " " , a . id ) for i := range primaryPoints { a . logPersister . Infof ( " " , a . id , & primaryPoints [ i ] ) } return false , nil } return true , nil }
analyzeWithCanaryPrimary returns false if canary deviates in the specified direction compared to primary.
analyzeWithCanaryPrimary returns false if canary deviates in the specified direction compared to primary .