kevinms/leakybucket-go

Strange Behaviour With Count

Closed this issue · 4 comments

Hey @kevinms

Love the work on this. Came across an interesting scenario that I can't explain.

The theory here is that I have something being added every 10 seconds. Therefore I have a rate of 0.1 set.

If I set the b.Add() to 1 then it matches the rate and the count is indeed 1 in every echo. If I set it to 2 every 10 seconds then surely (as the rate is set to 0.1) then the count should stabilise at 2. However it increases!

Maybe I am using this wrong but I want to count how many over a period and thought the leaky bucket algorithm was the right thing to use?

I've tried to track down the issue but cannot work it out!

func testFunction() {
	b := leakybucket.NewLeakyBucket(0.1, 100000)

	go func() {
		for {
			b.Add(1)
			log.Println("Current Count:", b.Count())
			time.Sleep(time.Second * 10)
		}
	}()
}

I think that behavior is correct for a leaky bucket.

If you add 2 tokens every 10 seconds, that is adding at a rate of 0.2 tokens/sec.

The bucket was configured to leak at 0.1 tokens/sec.

So, tokens are being added faster than they are removed (leaking). The bucket will slowly fill up until it is completely full. Each time you print, the count will be slightly larger.

Does that make sense? Hopefully, I understood your scenario correctly.

Maybe I am using this wrong but I want to count how many over a period and thought the leaky bucket algorithm was the right thing to use?

I'm not sure a leaky bucket is a good fit for your use case. Leaky buckets are often used for rate limiting, e.g. network traffic. It's properties allow occasional bursty traffic while sustained high traffic is throttled.

Another note here.

When tokens are added at a rate of 0.1 tokens/sec and leaking at the same rate, they do equalize out, but only over time. In your examples, it will take 10 real seconds to completely leak out.

If you print immediately after adding, almost no time has passed. So the count matches what you just added. But if you print after the sleep, (or just before the Add()), the count will be 0.

for {
	log.Println("Count before add:", b.Count())
	b.Add(1)
	log.Println("Count after add:", b.Count())

	time.Sleep(time.Second * 10)
}

I should really put together some better examples that show case how to use the library and how it works!

No this does make sense actually. I think I was being silly!

In the end I took inspiration from this but created a rate counter using a fixed interval period rather than something based on a leaky bucket.

Oh nice! That sounds fun to implement too!

I'm going to close out this issue.