Change:
import ( "testing" "time" <ins> "k8s.io/apimachinery/pkg/util/clock" </ins> ) func TestTimeBudget(t *testing.T) { <ins> fakeClock := clock.NewFakeClock(time.Now()) </ins> budget := &timeBudgetImpl{ <ins> clock:     fakeClock, </ins> budget:    time.Duration(0), <del> maxBudget: time.Duration(200), </del> <ins> maxBudget: 200 * time.Millisecond, refresh:   50 * time.Millisecond, last:      fakeClock.Now(), </ins> } if res := budget.takeAvailable(); res != time.Duration(0) { t.Errorf("Expected: %v, got: %v", time.Duration(0), res) } <del> budget.budget = time.Duration(100) if res := budget.takeAvailable(); res != time.Duration(100) { t.Errorf("Expected: %v, got: %v", time.Duration(100), res) </del> <ins> // wait for longer than the maxBudget nextTime := time.Now().Add(10 * time.Second) fakeClock.SetTime(nextTime) if res := budget.takeAvailable(); res != budget.maxBudget { t.Errorf("Expected: %v, got: %v", budget.maxBudget, res) } // add two refresh intervals to accumulate 2*refresh durations nextTime = nextTime.Add(2 * time.Second) fakeClock.SetTime(nextTime) if res := budget.takeAvailable(); res != 2*budget.refresh { t.Errorf("Expected: %v, got: %v", 2*budget.refresh, res) } // return one refresh duration to have only one refresh duration available // we didn't advanced on time yet budget.returnUnused(budget.refresh) if res := budget.takeAvailable(); res != budget.refresh { t.Errorf("Expected: %v, got: %v", budget.refresh, res) </ins> } <ins> // return a negative value to the budget // we didn't advanced on time yet budget.returnUnused(-time.Duration(50)) </ins> if res := budget.takeAvailable(); res != time.Duration(0) { t.Errorf("Expected: %v, got: %v", time.Duration(0), res) } <del> budget.returnUnused(time.Duration(50)) if res := budget.takeAvailable(); res != time.Duration(50) { t.Errorf("Expected: %v, got: %v", time.Duration(50), res) </del> <ins> // handle back in time problem with an empty budget nextTime = nextTime.Add(-2 * time.Minute) fakeClock.SetTime(nextTime) if res := budget.takeAvailable(); res != time.Duration(0) { t.Errorf("Expected: %v, got: %v", time.Duration(0), res) </ins> } <del> budget.budget = time.Duration(100) budget.returnUnused(-time.Duration(50)) if res := budget.takeAvailable(); res != time.Duration(100) { t.Errorf("Expected: %v, got: %v", time.Duration(100), res) </del> <ins> // wait for longer than the maxBudget // verify that adding a negative value didn't affected nextTime = nextTime.Add(10 * time.Minute) fakeClock.SetTime(nextTime) if res := budget.takeAvailable(); res != budget.maxBudget { t.Errorf("Expected: %v, got: %v", budget.maxBudget, res) </ins> } <del> // test overflow. budget.returnUnused(time.Duration(500)) if res := budget.takeAvailable(); res != time.Duration(200) { t.Errorf("Expected: %v, got: %v", time.Duration(200), res) </del> <ins> // handle back in time problem with time on the budget budget.returnUnused(10 * time.Second) nextTime = nextTime.Add(-2 * time.Minute) fakeClock.SetTime(nextTime) if res := budget.takeAvailable(); res != budget.maxBudget { t.Errorf("Expected: %v, got: %v", budget.maxBudget, res) </ins> } }

Discussion:
Instead of relying on the clock: we should do something similar to the golang rate-limitter is doing:
/sig api-machinery /priority important-longterm
/help
This request has been marked as needing help from a contributor. Please ensure the request meets the requirements listed . If this request no longer meets these requirements, the label can be removed by commenting with the command. <detailsIn response to : /help Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
Hi, I would like to help on this.
/assign Thanks! /triage accepted
can you please provide more context or some guidance on how to proceed?
Sorry for delayed response. - what we should do is to basically: instead of having an additional go-routine that every 1s refreshes the budget change it roughly to: (a) keep the value of budget and the last time when we were refreshing it (b) on takeAvailable method: first recompute takeAvailable, which will be "min(current budget + time elapsed since last refresh * refreshValue, maxAllowedBudget) update last refresh time to now return that value [We also would need to update the existing tests to match the new logic.] - would you be willing to work on it?
-- I can take this up. One question I had was should the method rely on or the current time should be passed on by the caller like in the following: Having the caller pass the time completely removes the dependency on the clock at least for the implementation.
/assign
Let's may be start with: passing on budget creation to it relying on
-- thank you for the thoughts. I drafted a rough implementation in which still depends on . I will make the change as you suggested and rely solely on a instance passed on initialization. I have to rethink the testing strategy a bit, possibly using a .
Yes - FakeClock is the way to test this.
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-contributor-experience at . /lifecycle stale
/remove-lifecycle stale
Issues go stale after 90d of inactivity.

Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-contributor-experience at . /lifecycle stale
Stale issues rot after 30d of inactivity. Mark the issue as fresh with . Rotten issues close after an additional 30d of inactivity. If this issue is safe to close now please do so with . Send feedback to sig-contributor-experience at . /lifecycle rotten
Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . Send feedback to sig-contributor-experience at . /close
Closing this issue. <detailsIn response to : Rotten issues close after 30d of inactivity. Reopen the issue with . Mark the issue as fresh with . /close Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
Reopening - this didn't happen.