Browse Source

prototype of accumulateBy logic with negative asr window

Alejandro 4 years ago
parent
commit
7c04602d7d
2 changed files with 119 additions and 49 deletions
  1. 60 35
      pkg/kubecost/allocation.go
  2. 59 14
      pkg/kubecost/allocation_test.go

+ 60 - 35
pkg/kubecost/allocation.go

@@ -2269,52 +2269,62 @@ func (asr *AllocationSetRange) AccumulateBy(resolution time.Duration) (*Allocati
 	asr.Lock()
 	asr.Lock()
 	defer asr.Unlock()
 	defer asr.Unlock()
 
 
-	fmt.Printf("asrWindow: %v", asr.Window())
-	// fmt.Println(time.Hour > 61*time.Minute)
-	// if asr.Window().IsEmpty() {
-	// 	return asr, nil
-	// }
+	fmt.Printf("asr window duration: %v\n", asr.window().Duration())
+	fmt.Printf("resolution: %v\n", resolution)
 
 
-	// Call total accumulate func if resolution is greater than total window duration
-	// if resolution > asr.Window().Duration() {
-	// 	as, err := asr.Accumulate()
-	// 	if err != nil {
-	// 		return nil, err
-	// 	}
-	// 	allocSetRange.allocations = append(allocSetRange.allocations, as)
-	// 	return allocSetRange, nil
-	// }
+	// use window() with no lock
+	if asr.window().IsEmpty() {
+		return asr, nil
+	}
 
 
-	// if asrResolution < 1 hour then there is not enough data to accumulate
-	// if as.allocations end - start > resolution then no need, it's already accumulated. Requery to make more granular in the future?
+	// Call total accumulate func if resolution is greater than total window duration
+	if resolution > asr.window().Duration() {
+		// unable to acquire lock here if old accumulate is called
+		for _, as := range asr.allocations {
+			allocSet, err = allocSet.accumulate(as)
+			if err != nil {
+				return nil, err
+			}
+		}
+		allocSetRange.allocations = append(allocSetRange.allocations, allocSet)
+		return allocSetRange, nil
+	}
 
 
-	// asrResolution := asr.Window().Duration()
-	// fmt.Printf("asrResolution: %s", asrResolution)
-	// if asrResolution <= time.Hour || asrResolution < resolution {
-	// 	return asr, nil
-	// }
+	asrWindow := asr.window()
+	asrResolution := asrWindow.Duration()
 
 
-	// if(resolution)
+	// if asrResolution < 1 hour then there is not enough data to accumulate
+	// if as.allocations(end - start) > resolution then no need, it's already accumulated. Requery to make more granular in the future?
+	// check this again?
+	if asrResolution <= time.Hour || asrResolution < resolution {
+		return asr, nil
+	}
 
 
 	// do not compound
 	// do not compound
 	// check as.window and accumulate till windowSum == time.duration wanted -> add accumulated set to allocSetRange
 	// check as.window and accumulate till windowSum == time.duration wanted -> add accumulated set to allocSetRange
-
-	// var currAccumulatedSum time.Duration
+	var currAccumulatedSum time.Duration
 	for _, as := range asr.allocations {
 	for _, as := range asr.allocations {
 		//What time group are we in? Is time group bigger than duration?
 		//What time group are we in? Is time group bigger than duration?
-
 		allocSet, err = allocSet.accumulate(as)
 		allocSet, err = allocSet.accumulate(as)
 		if err != nil {
 		if err != nil {
 			return nil, err
 			return nil, err
 		}
 		}
-		// currAccumulatedSum += allocSet.Window.Duration()
+		currAccumulatedSum += allocSet.Window.Duration()
 
 
-		// if currAccumulatedSum >= resolution { // || check if end of asr to sum the final as
-		allocSetRange.allocations = append(allocSetRange.allocations, allocSet)
-		// make allocSet empty somehow
-		// allocSet = NewAllocationSet(nil, nil, nil)
-		// currAccumulatedSum = 0
-		// }
+		fmt.Printf("asr end %v as end %v\n", asrWindow.end, allocSet.Window.end)
+		// two ways to get end of asr window
+		// 1. check if last set of asr
+		// 2. check if set window.end is asr.window.end
+		// both require no lock to be present
+		// option 1: 2 asr.window methods
+		// option 2: 2 window.length methods
+
+		// check if end of asr to sum the final set too
+		if currAccumulatedSum >= resolution || NewWindow(nil, asrWindow.end).Equal(NewWindow(nil, allocSet.Window.end)) {
+			allocSetRange.allocations = append(allocSetRange.allocations, allocSet)
+			allocSet = &AllocationSet{allocations: map[string]*Allocation{}}
+			currAccumulatedSum = 0
+		}
 	}
 	}
 
 
 	return allocSetRange, nil
 	return allocSetRange, nil
@@ -2322,7 +2332,6 @@ func (asr *AllocationSetRange) AccumulateBy(resolution time.Duration) (*Allocati
 
 
 // AggregateBy aggregates each AllocationSet in the range by the given
 // AggregateBy aggregates each AllocationSet in the range by the given
 // properties and options.
 // properties and options.
-// func (as *AllocationSet) AggregateBy(aggregateBy []string, options *AllocationAggregationOptions) error {
 func (asr *AllocationSetRange) AggregateBy(aggregateBy []string, options *AllocationAggregationOptions) error {
 func (asr *AllocationSetRange) AggregateBy(aggregateBy []string, options *AllocationAggregationOptions) error {
 	aggRange := &AllocationSetRange{allocations: []*AllocationSet{}}
 	aggRange := &AllocationSetRange{allocations: []*AllocationSet{}}
 
 
@@ -2489,13 +2498,29 @@ func (asr *AllocationSetRange) UTCOffset() time.Duration {
 // Window returns the full window that the AllocationSetRange spans, from the
 // Window returns the full window that the AllocationSetRange spans, from the
 // start of the first AllocationSet to the end of the last one.
 // start of the first AllocationSet to the end of the last one.
 func (asr *AllocationSetRange) Window() Window {
 func (asr *AllocationSetRange) Window() Window {
-	if asr == nil || asr.Length() == 0 {
+	asr.Lock()
+	defer asr.Unlock()
+	return asr.window()
+}
+
+func (asr *AllocationSetRange) window() Window {
+	var length int
+
+	if asr == nil || asr.allocations == nil {
+		length = 0
+	} else {
+		length = len(asr.allocations)
+	}
+
+	if asr == nil || length == 0 {
 		return NewWindow(nil, nil)
 		return NewWindow(nil, nil)
 	}
 	}
 
 
 	start := asr.allocations[0].Start()
 	start := asr.allocations[0].Start()
-	end := asr.allocations[asr.Length()-1].End()
+	end := asr.allocations[length-1].End()
 
 
+	fmt.Printf("start %v\n", start)
+	fmt.Printf("end %v\n", end)
 	return NewWindow(&start, &end)
 	return NewWindow(&start, &end)
 }
 }
 
 

+ 59 - 14
pkg/kubecost/allocation_test.go

@@ -2069,14 +2069,17 @@ func TestAllocationSetRange_Accumulate(t *testing.T) {
 }
 }
 
 
 func TestAllocationSetRange_AccumulateBy(t *testing.T) {
 func TestAllocationSetRange_AccumulateBy(t *testing.T) {
+	ago4d := time.Now().UTC().Truncate(day).Add(-4 * day)
+	ago3d := time.Now().UTC().Truncate(day).Add(-3 * day)
 	ago2d := time.Now().UTC().Truncate(day).Add(-2 * day)
 	ago2d := time.Now().UTC().Truncate(day).Add(-2 * day)
 	yesterday := time.Now().UTC().Truncate(day).Add(-day)
 	yesterday := time.Now().UTC().Truncate(day).Add(-day)
 	today := time.Now().UTC().Truncate(day)
 	today := time.Now().UTC().Truncate(day)
 	tomorrow := time.Now().UTC().Truncate(day).Add(day)
 	tomorrow := time.Now().UTC().Truncate(day).Add(day)
-	dur := time.Hour
+	dur := time.Hour * 24 * 2
 
 
 	// Accumulating any combination of nil and/or empty set should result in empty set
 	// Accumulating any combination of nil and/or empty set should result in empty set
-	fmt.Println("test 1")
+	fmt.Println("======================")
+	fmt.Println("test 1 nil set")
 	result, err := NewAllocationSetRange(nil).AccumulateBy(dur)
 	result, err := NewAllocationSetRange(nil).AccumulateBy(dur)
 	for _, as := range result.allocations {
 	for _, as := range result.allocations {
 		if err != nil {
 		if err != nil {
@@ -2087,7 +2090,8 @@ func TestAllocationSetRange_AccumulateBy(t *testing.T) {
 		}
 		}
 	}
 	}
 
 
-	fmt.Println("test 2")
+	fmt.Println("======================")
+	fmt.Println("test 2 nil sets")
 	result, err = NewAllocationSetRange(nil, nil).AccumulateBy(dur)
 	result, err = NewAllocationSetRange(nil, nil).AccumulateBy(dur)
 	for _, as := range result.allocations {
 	for _, as := range result.allocations {
 		if err != nil {
 		if err != nil {
@@ -2098,6 +2102,8 @@ func TestAllocationSetRange_AccumulateBy(t *testing.T) {
 		}
 		}
 	}
 	}
 
 
+	fmt.Println("======================")
+	fmt.Println("test 1 set 2d long")
 	result, err = NewAllocationSetRange(NewAllocationSet(yesterday, today)).AccumulateBy(dur)
 	result, err = NewAllocationSetRange(NewAllocationSet(yesterday, today)).AccumulateBy(dur)
 	for _, as := range result.allocations {
 	for _, as := range result.allocations {
 		if err != nil {
 		if err != nil {
@@ -2108,6 +2114,8 @@ func TestAllocationSetRange_AccumulateBy(t *testing.T) {
 		}
 		}
 	}
 	}
 
 
+	fmt.Println("======================")
+	fmt.Println("test 2 nil sets 2 1d sets")
 	result, err = NewAllocationSetRange(nil, NewAllocationSet(ago2d, yesterday), nil, NewAllocationSet(today, tomorrow), nil).AccumulateBy(dur)
 	result, err = NewAllocationSetRange(nil, NewAllocationSet(ago2d, yesterday), nil, NewAllocationSet(today, tomorrow), nil).AccumulateBy(dur)
 	for _, as := range result.allocations {
 	for _, as := range result.allocations {
 		if err != nil {
 		if err != nil {
@@ -2118,21 +2126,58 @@ func TestAllocationSetRange_AccumulateBy(t *testing.T) {
 		}
 		}
 	}
 	}
 
 
-	todayAS := NewAllocationSet(today, tomorrow)
-	todayAS.Set(NewMockUnitAllocation("", today, day, nil))
-
-	yesterdayAS := NewAllocationSet(yesterday, today)
-	yesterdayAS.Set(NewMockUnitAllocation("", yesterday, day, nil))
+	fmt.Println("======================")
+	fmt.Println("test 2 nil sets 2 1d sets")
+	result, err = NewAllocationSetRange(nil, NewAllocationSet(ago2d, yesterday), nil, NewAllocationSet(today, tomorrow), nil).AccumulateBy(dur)
+	for _, as := range result.allocations {
+		if err != nil {
+			t.Fatalf("unexpected error accumulating nil AllocationSetRange: %s", err)
+		}
+		if !as.IsEmpty() {
+			t.Fatalf("accumulating nil AllocationSetRange: expected empty; actual %s", result)
+		}
+	}
 
 
-	// Accumulate non-nil with nil should result in copy of non-nil, regardless of order
-	result, err = NewAllocationSetRange(nil, todayAS).AccumulateBy(dur)
-	if err != nil {
-		t.Fatalf("unexpected error accumulating AllocationSetRange of length 1: %s", err)
+	fmt.Println("======================")
+	fmt.Println("test 2 1d sets")
+	result, err = NewAllocationSetRange(NewAllocationSet(ago2d, yesterday), NewAllocationSet(today, tomorrow), nil).AccumulateBy(dur)
+	for _, as := range result.allocations {
+		if err != nil {
+			t.Fatalf("unexpected error accumulating nil AllocationSetRange: %s", err)
+		}
+		if !as.IsEmpty() {
+			t.Fatalf("accumulating nil AllocationSetRange: expected empty; actual %s", result)
+		}
 	}
 	}
-	if result == nil {
-		t.Fatalf("accumulating AllocationSetRange: expected AllocationSet; actual %s", result)
+
+	fmt.Println("======================")
+	fmt.Println("test 3 1d sets")
+	result, err = NewAllocationSetRange(NewAllocationSet(ago4d, ago3d), NewAllocationSet(ago2d, yesterday), NewAllocationSet(today, tomorrow), nil).AccumulateBy(dur)
+	for _, as := range result.allocations {
+		if err != nil {
+			t.Fatalf("unexpected error accumulating nil AllocationSetRange: %s", err)
+		}
+		if !as.IsEmpty() {
+			t.Fatalf("accumulating nil AllocationSetRange: expected empty; actual %s", result)
+		}
 	}
 	}
+	// todayAS := NewAllocationSet(today, tomorrow)
+	// todayAS.Set(NewMockUnitAllocation("", today, day, nil))
+
+	// yesterdayAS := NewAllocationSet(yesterday, today)
+	// yesterdayAS.Set(NewMockUnitAllocation("", yesterday, day, nil))
+
+	// fmt.Println("test 5")
+	// // Accumulate non-nil with nil should result in copy of non-nil, regardless of order
+	// result, err = NewAllocationSetRange(nil, todayAS).AccumulateBy(dur)
+	// if err != nil {
+	// 	t.Fatalf("unexpected error accumulating AllocationSetRange of length 1: %s", err)
+	// }
+	// if result == nil {
+	// 	t.Fatalf("accumulating AllocationSetRange: expected AllocationSet; actual %s", result)
+	// }
 
 
+	// fmt.Println("test 6")
 	// for _, as := range result.allocations {
 	// for _, as := range result.allocations {
 	// 	if as.TotalCost() != 6.0 {
 	// 	if as.TotalCost() != 6.0 {
 	// 		t.Fatalf("accumulating AllocationSetRange: expected total cost 6.0; actual %f", as.TotalCost())
 	// 		t.Fatalf("accumulating AllocationSetRange: expected total cost 6.0; actual %f", as.TotalCost())