func()

in pkg/controllers/member/endpointsliceimport/controller.go [359:430]


func (r *Reconciler) observeMetrics(ctx context.Context, endpointSliceImport *fleetnetv1alpha1.EndpointSliceImport, startTime time.Time) error {
	// Check if a metric data point has been observed for the current generation of the object; this helps guard
	// against repeated observation of metric data points for the same generation of an object due to no-op
	// reconciliations (e.g. resyncs, untracked changes).
	lastObservedGeneration, ok := endpointSliceImport.Annotations[metrics.MetricsAnnotationLastObservedGeneration]
	currentGenerationStr := fmt.Sprintf("%d", endpointSliceImport.Spec.EndpointSliceReference.Generation)
	// isFirstImport flag signals if the endpointSlice has been imported before. This flag is for the purpose of
	// filtering out any outlier caused by late service imports: service export/import is two-phase op, in which either
	// phase can be performed individually in no specific order; it is possible for a user to export a service first
	// and then import it as a MCS at a much later time, and consequently a significant delay, through no fault
	// of Fleet networking controllers, will be observed when importing endpointSlices from the service for the
	// first time.
	// Note that technically speaking there is no easy way for controllers to ascertain whether (and exactly how much)
	// the factor of late imports plays a part in the export/import latency.
	isFirstImport := !ok
	if ok && lastObservedGeneration == currentGenerationStr {
		// A data point has been observed for this generation; skip the observation.
		return nil
	}

	// Observe a new data point.

	// Annotate the object to track the last observed generation; this must happen before the actual observation.
	if endpointSliceImport.Annotations == nil {
		// Initialize the annotation map if it is empty.
		endpointSliceImport.Annotations = map[string]string{}
	}
	endpointSliceImport.Annotations[metrics.MetricsAnnotationLastObservedGeneration] = currentGenerationStr
	if err := r.HubClient.Update(ctx, endpointSliceImport); err != nil {
		return err
	}

	// Skip the observation if the exportedSince field is empty in the object reference.
	// Note that in most cases this branch should never run as the Fleet networking controllers will always assign a
	// timestamp for each exported object.
	if endpointSliceImport.Spec.EndpointSliceReference.ExportedSince.IsZero() {
		klog.V(4).InfoS("exportedSince timestamp is absent; endpointSlice export/import duration data point is not collected",
			"endpointSliceImport", klog.KObj(endpointSliceImport))
		return nil
	}
	timeSpent := startTime.Sub(endpointSliceImport.Spec.EndpointSliceReference.ExportedSince.Time).Milliseconds()
	// Under some rare circumstances (such as time sync not being configured properly across clusters), it could
	// happen that the export timestamp of an EndpointSlice appears later than its import timestamp. Unfortunately,
	// clock discrepancies are out of Fleet networking's control and there is not an easy way to determine how
	// much clocks drift from each other and if (when) it will recover. To avoid negative outliers affecting
	// data analysis, this controller assigns a constant of exactly 1 second when the calculated duration does
	// not make sense.
	if timeSpent <= 0 {
		timeSpent = time.Second.Milliseconds() * 1
		klog.V(4).Info("A negative endpointSlice export/import duration data point has been observed; time sync might be out of order",
			"serviceNamespacedName", endpointSliceImport.Spec.OwnerServiceReference.NamespacedName,
			"endpointSliceNamespacedName", endpointSliceImport.Spec.EndpointSliceReference.NamespacedName,
			"originClusterID", endpointSliceImport.Spec.EndpointSliceReference.ClusterID,
			"destinationClusterID", r.MemberClusterID,
			"isFirstImport", isFirstImport)
	}
	// Similarly, to avoid large outliers skewing the stats (e.g. averages), this controller caps the data point
	// to a constant value.
	if timeSpent > int64(metrics.ExportDurationRightBound) {
		timeSpent = int64(metrics.ExportDurationRightBound)
	}
	endpointSliceExportImportDuration.
		WithLabelValues(endpointSliceImport.Spec.EndpointSliceReference.ClusterID, r.MemberClusterID, fmt.Sprintf("%t", isFirstImport)).
		Observe(float64(timeSpent))
	// TO-DO (chenyu1): Remove the metric logs when histogram metrics are supported in the backend.
	klog.V(2).InfoS("endpointSliceExportImportDurationMilliseconds",
		"value", timeSpent,
		"originClusterID", endpointSliceImport.Spec.EndpointSliceReference.ClusterID,
		"destinationClusterID", r.MemberClusterID,
		"isFirstImport", isFirstImport)
	return nil
}