ソースを参照

Add IntervalPoints sort logic + cleanup

Kaelan Patel 4 年 前
コミット
f948bcb4ce
3 ファイル変更45 行追加40 行削除
  1. 2 3
      pkg/costmodel/allocation.go
  2. 42 37
      pkg/costmodel/intervals.go
  3. 1 0
      pkg/costmodel/intervals_test.go

+ 2 - 3
pkg/costmodel/allocation.go

@@ -415,6 +415,7 @@ func (cm *CostModel) ComputeAllocation(start, end time.Time, resolution time.Dur
 				}
 			}
 
+			// We only need to look at one alloc per pod
 			break
 		}
 
@@ -432,8 +433,6 @@ func (cm *CostModel) ComputeAllocation(start, end time.Time, resolution time.Dur
 		// Determine coefficients for each PVC-pod relation.
 		sharedPVCCostCoefficientMap[pvcKey] = getPVCCostCoefficients(intervals, podIntervalMap)
 
-		//sharedPVCCostCoefficientMap[pvcKey] = pvcCostCoefficientMap
-
 	}
 
 	// Identify unmounted PVs (PVs without PVCs) and add one Allocation per
@@ -486,7 +485,7 @@ func (cm *CostModel) ComputeAllocation(start, end time.Time, resolution time.Dur
 
 					// Scale PV cost by PVC sharing coefficient.
 					if coeffComponents, ok := sharedPVCCostCoefficientMap[pvcKey][podKey]; ok {
-						cost *= getCoefficient(coeffComponents)
+						cost *= getCoefficientFromComponents(coeffComponents)
 					} else {
 						log.Warningf("CostModel.ComputeAllocation: allocation %s and PVC %s have relation but no coeff", alloc.Name, pvc.Name)
 					}

+ 42 - 37
pkg/costmodel/intervals.go

@@ -16,13 +16,24 @@ type IntervalPoint struct {
 	Key       podKey
 }
 
-// CoefficientComponent is a representitive struct holding two fields which describe an interval
-// as part of a single number cost coefficient calculation:
-// 1. Proportion: The division of cost based on how many pods were running between those points
-// 2. Time: The ratio of the time between those points to the total time that pod was running
-type CoefficientComponent struct {
-	Proportion float64
-	Time       float64
+// IntervalPoints describes a slice of IntervalPoint structs
+type IntervalPoints []IntervalPoint
+
+// Requisite functions for implementing sort.Sort for
+// IntervalPointList
+func (ips IntervalPoints) Len() int {
+	return len(ips)
+}
+
+func (ips IntervalPoints) Less(i, j int) bool {
+	if ips[i].Time.Equal(ips[j].Time) {
+		return ips[i].PointType == "start" && ips[j].PointType == "end"
+	}
+	return ips[i].Time.Before(ips[j].Time)
+}
+
+func (ips IntervalPoints) Swap(i, j int) {
+	ips[i], ips[j] = ips[j], ips[i]
 }
 
 // NewIntervalPoint creates and returns a new IntervalPoint instance with given parameters.
@@ -34,12 +45,21 @@ func NewIntervalPoint(time time.Time, pointType string, key podKey) IntervalPoin
 	}
 }
 
+// CoefficientComponent is a representitive struct holding two fields which describe an interval
+// as part of a single number cost coefficient calculation:
+// 1. Proportion: The division of cost based on how many pods were running between those points
+// 2. Time: The ratio of the time between those points to the total time that pod was running
+type CoefficientComponent struct {
+	Proportion float64
+	Time       float64
+}
+
 // getIntervalPointFromWindows takes a map of podKeys to windows
 // and returns a sorted list of IntervalPoints representing the
 // starts and ends of all those windows.
-func getIntervalPointsFromWindows(windows map[podKey]kubecost.Window) []IntervalPoint {
+func getIntervalPointsFromWindows(windows map[podKey]kubecost.Window) IntervalPoints {
 
-	var intervals []IntervalPoint
+	var intervals IntervalPoints
 
 	for podKey, podInterval := range windows {
 
@@ -50,29 +70,16 @@ func getIntervalPointsFromWindows(windows map[podKey]kubecost.Window) []Interval
 
 	}
 
-	sortIntervalPoints(intervals)
+	sort.Sort(intervals)
 
 	return intervals
 
 }
 
-// sortIntervalPoints sorts a list of IntervalPoints from earliest
-// to latest IntervalPoint.Time. In the case that two IntervalPoints
-// have the same time, the point with Type "start" is treated as coming
-// before the "end".
-func sortIntervalPoints(intervals []IntervalPoint) {
-	sort.Slice(intervals, func(i, j int) bool {
-		if intervals[i].Time.Equal(intervals[j].Time) {
-			return intervals[i].PointType == "start" && intervals[j].PointType == "end"
-		}
-		return intervals[i].Time.Before(intervals[j].Time)
-	})
-}
-
 // getPVCCostCoefficients gets a coefficient which represents the scale
 // factor that each PVC in a pvcIntervalMap and corresponding slice of
 // IntervalPoints intervals uses to calculate a cost for that PVC's PV.
-func getPVCCostCoefficients(intervals []IntervalPoint, pvcIntervalMap map[podKey]kubecost.Window) map[podKey][]CoefficientComponent {
+func getPVCCostCoefficients(intervals IntervalPoints, pvcIntervalMap map[podKey]kubecost.Window) map[podKey][]CoefficientComponent {
 
 	pvcCostCoefficientMap := make(map[podKey][]CoefficientComponent)
 
@@ -80,8 +87,6 @@ func getPVCCostCoefficients(intervals []IntervalPoint, pvcIntervalMap map[podKey
 	// such that the individual coefficient components are preserved for
 	// testing purposes.
 
-	activePods := 1.0
-
 	activeKeys := map[podKey]struct{}{
 		intervals[0].Key: struct{}{},
 	}
@@ -96,25 +101,25 @@ func getPVCCostCoefficients(intervals []IntervalPoint, pvcIntervalMap map[podKey
 		// If the current point happens at a later time than the previous point
 		if !point.Time.Equal(prevPoint.Time) {
 			for key := range activeKeys {
-				pvcCostCoefficientMap[key] = append(
-					pvcCostCoefficientMap[key],
-					CoefficientComponent{
-						Time:       point.Time.Sub(prevPoint.Time).Minutes() / pvcIntervalMap[key].Duration().Minutes(),
-						Proportion: 1.0 / activePods,
-					},
-				)
+				if pvcIntervalMap[key].Duration().Minutes() != 0 {
+					pvcCostCoefficientMap[key] = append(
+						pvcCostCoefficientMap[key],
+						CoefficientComponent{
+							Time:       point.Time.Sub(prevPoint.Time).Minutes() / pvcIntervalMap[key].Duration().Minutes(),
+							Proportion: 1.0 / float64(len(activeKeys)),
+						},
+					)
+				}
 			}
 		}
 
 		// If the point was a start, increment and track
 		if point.PointType == "start" {
-			activePods += 1
 			activeKeys[point.Key] = struct{}{}
 		}
 
 		// If the point was an end, decrement and stop tracking
 		if point.PointType == "end" {
-			activePods -= 1
 			delete(activeKeys, point.Key)
 		}
 
@@ -123,10 +128,10 @@ func getPVCCostCoefficients(intervals []IntervalPoint, pvcIntervalMap map[podKey
 	return pvcCostCoefficientMap
 }
 
-// getCoefficient takes the components of a PVC-pod PV cost coefficient
+// getCoefficientFromComponents takes the components of a PVC-pod PV cost coefficient
 // determined by getPVCCostCoefficient and gets the resulting single
 // floating point coefficient.
-func getCoefficient(coefficientComponents []CoefficientComponent) float64 {
+func getCoefficientFromComponents(coefficientComponents []CoefficientComponent) float64 {
 
 	coefficient := 0.0
 

+ 1 - 0
pkg/costmodel/intervals_test.go

@@ -127,6 +127,7 @@ func TestGetIntervalPointsFromWindows(t *testing.T) {
 
 	for _, testCase := range cases {
 		t.Run(testCase.name, func(t *testing.T) {
+
 			result := getIntervalPointsFromWindows(testCase.pvcIntervalMap)
 
 			if len(result) != len(testCase.expected) {