func detectDegradations()

in pkg/degradation-detector/degradationDetector.go [39:124]


func detectDegradations(values []int, builds []string, timestamps []int64, analysisSettings analysisSettings) []Degradation {
	degradations := make([]Degradation, 0)

	if analysisSettings.GetAnalysisKind() == ThresholdAnalysis {
		return detectThresholdExceed(values, builds, timestamps, analysisSettings)
	}

	minimumSegmentLength := analysisSettings.GetMinimumSegmentLength()
	if minimumSegmentLength == 0 {
		minimumSegmentLength = 5
	}
	medianDifference := analysisSettings.GetMedianDifferenceThreshold()
	if medianDifference == 0 {
		medianDifference = 10
	}

	effectSizeThreshold := analysisSettings.GetEffectSizeThreshold()
	if effectSizeThreshold == 0 {
		effectSizeThreshold = 2
	}

	changePoints := statistic.GetChangePointIndexes(values, min(5, len(values)/2))
	segments := GetSegmentsBetweenChangePoints(changePoints, values)
	if len(segments) < 2 {
		slog.Debug("no significant change points were detected")
		return degradations
	}
	lastSegment := segments[len(segments)-1]
	if len(lastSegment) < minimumSegmentLength {
		slog.Info("last segment is too short")
		return degradations
	}

	skippedSegments := 0
	for i := len(segments) - 2; i >= 0 && skippedSegments < 4; i-- {
		if len(segments[i]) < minimumSegmentLength {
			skippedSegments++
			continue
		}

		currentCenter, err := pragmastat.Center(lastSegment)
		if err != nil {
			skippedSegments++
			continue
		}
		previousCenter, err := pragmastat.Center(segments[i])
		if err != nil {
			skippedSegments++
			continue
		}

		ratio := currentCenter / previousCenter

		percentageChange := math.Abs((ratio - 1) * 100)
		absoluteChange := math.Abs(currentCenter - previousCenter)

		if absoluteChange < 10 || percentageChange < medianDifference {
			break
		}

		es := statistic.EffectSize(lastSegment, segments[i])
		if es < effectSizeThreshold {
			break
		}

		isDegradation := currentCenter > previousCenter
		reportType := analysisSettings.GetReportType()

		if !isDegradation && reportType == DegradationEvent {
			break
		}
		if isDegradation && reportType == ImprovementEvent {
			break
		}
		index := changePoints[len(segments)-2]
		degradations = append(degradations, Degradation{
			Build:         builds[index],
			timestamp:     timestamps[index],
			medianValues:  CenterValues{previousValue: previousCenter, newValue: currentCenter},
			IsDegradation: isDegradation,
		})
		break
	}

	return degradations
}