Repository: vllm-project/aibrix
File: pkg/controller/podautoscaler/metrics/utils.go (Go)

Code before:
	}

	// TODO(jiaxin.shan): convert this raw metric to PodMetrics
	return nil, currentTimestamp, nil
}

func GetMetricsFromPods(ctx context.Context, fetcher MetricFetcher, pods []corev1.Pod, source autoscalingv1alpha1.MetricSource) ([]float64, error) {
	metrics := make([]float64, 0, len(pods))
	for _, pod := range pods {
		// TODO: Let's optimize the performance for multi-metrics later.
		metric, err := fetcher.FetchPodMetrics(ctx, pod, source)
		if err != nil {
			klog.ErrorS(err, "Failed to get metrics from pod", "pod", klog.KObj(&pod))
			// If we fail to get metrics for a pod, we should continue to get metrics from other pods.
			continue
		}
		metrics = append(metrics, metric)
	}
	if len(metrics) == 0 && len(pods) > 0 {
		return nil, fmt.Errorf("failed to get metrics from any of the %d pods", len(pods))
	}
	return metrics, nil
}

func GetMetricFromSource(ctx context.Context, fetcher MetricFetcher, source autoscalingv1alpha1.MetricSource) (float64, error) {
	endpoint := source.Endpoint

	// If port specified, try override.
	if source.Port != "" {
		u := url.URL{Host: source.Endpoint}
		if u.Port() == "" {
			endpoint = fmt.Sprintf("%s:%s", u.Hostname(), source.Port)
		}
	}
	return fetcher.FetchMetric(ctx, source.ProtocolType, endpoint, source.Path, source.TargetMetric)
}


Review comment (bug):
can you help double check whether client side has better handling on the case `len(metrics) != len(pods)`. I think `len(metrics) == 0 && len(pods) > 0` is one of the special case. What if partial pods failed?  I am a little bit concern on the client logic if client need to iterate metric slice but the length can not be aligned.

Code after:
	}

	// TODO(jiaxin.shan): convert this raw metric to PodMetrics
	return nil, currentTimestamp, nil
}

func GetMetricsFromPods(ctx context.Context, fetcher MetricFetcher, pods []corev1.Pod, source autoscalingv1alpha1.MetricSource) ([]float64, error) {
	metrics := make([]float64, 0, len(pods))
	var failedPods []string
	var lastErrorMessage string
	for _, pod := range pods {
		// TODO: Let's optimize the performance for multi-metrics later.
		metric, err := fetcher.FetchPodMetrics(ctx, pod, source)
		if err != nil {
			lastErrorMessage = err.Error()
			failedPods = append(failedPods, fmt.Sprintf("%s/%s", pod.Namespace, pod.Name))
			continue
		}
		metrics = append(metrics, metric)
	}
	// If we fail to get metrics from some pods, we should log a warning message.
	if len(metrics) < len(pods) {
		klog.Warningf("failed to get metrics from some pods: got %d/%d metrics. Failed pods: %v, with last error: %v", len(metrics), len(pods), failedPods, lastErrorMessage)
	}
	// If we fail to get metrics from all pods, we should return an error.
	if len(metrics) == 0 && len(pods) > 0 {
		return nil, fmt.Errorf("failed to get metrics from any of the %d pods", len(pods))
	}
	return metrics, nil
}

func GetMetricFromSource(ctx context.Context, fetcher MetricFetcher, source autoscalingv1alpha1.MetricSource) (float64, error) {
	endpoint := source.Endpoint

	// If port specified, try override.
	if source.Port != "" {
		u := url.URL{Host: source.Endpoint}
		if u.Port() == "" {
			endpoint = fmt.Sprintf("%s:%s", u.Hostname(), source.Port)
		}
	}
	return fetcher.FetchMetric(ctx, source.ProtocolType, endpoint, source.Path, source.TargetMetric)
}