Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@vasilmkd
Copy link
Contributor

Also fix potential token overflow.

Should be enough to close #1027.

@iravid
Copy link
Member

iravid commented Jul 19, 2019

@vasilmkd would it be possible to add burst as a default parameter on all the variants instead of a separate variant?

@vasilmkd
Copy link
Contributor Author

The current implementation of the token bucket algorithm should now closely match the one described here: Token Bucket Wikipedia.

@vasilmkd vasilmkd changed the title Add ZStream#throttleEnforceBurst, ZStream#throttleEnforceBurstM Add burst control to the existing Stream throttling combinators Jul 20, 2019
_ <- assertPositive(units).toManaged_
current <- clock.currentTime(TimeUnit.NANOSECONDS).toManaged_
bucket <- Ref.make((units, current)).toManaged_
def checkTokens(sum: Long, max: Long): Long = if (sum < 0) max else math.min(sum, max)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question: when sum < 0, should we subtract it from max? Otherwise when the bucket is depleted, there is always constant amount of max available in addition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I'll fix this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, on second thought, the checkTokens function is called to guard against overflow. sum is computed exclusively from positive values, whose total value might exceed Long.MaxValue (which is what the if statement checks). If that happens, we want max tokens to be available, otherwise, if we are dealing with positive values (sum and max), just get the minimum value.

@iravid
Copy link
Member

iravid commented Jul 20, 2019

Also, I believe the default value for max should reflect no burst capacity, so Long.MaxValue is probably not a good choice for that.

@vasilmkd
Copy link
Contributor Author

There are 2 ways to do this.

  1. Use max as the maximum number of tokens allowed at any time in the bucket (currently implemented). At the moment, the default value of Long.MaxValue enables an average bandwidth of units / duration. In order to restrict burst to 0, and offer at best units / duration bandwidth, one needs to set max to be equal to units. I personally think that having the maximum number of tokens be configurable is more user friendly than the second option. However, the no burst default needs to be implemented as an overloaded method, since max cannot be initialized to units in the type signature.

  2. Use threshold as a number of tokens which can be added to units when burst occurs. In the no burst default, it would be an argument with default value 0. This approach however, increases the danger of overflow, as two values of type Long need to be added together.

Interested to hear your thoughts on this. I'm happy to implement any of them.

@vasilmkd
Copy link
Contributor Author

Idea.

I can implement the second proposal from the last comment by wrapping the existing functions in
throttleEnforceM(units: Long, duration: Duration, burst: Long = 0): ZManaged[ZSink] which simply delegates to the inner method throttleEnforceM(units, duration, units + burst or Long.MaxValue in case of overflow) (the current implementaion).

That way we get the semantics of the second proposal with the "overflow safety" of the first one, and as a bonus, it requires minimal code changes.

@iravid
Copy link
Member

iravid commented Jul 21, 2019

Hi @vasilmkd, could you explain why you say that sum is computed exclusively of positive values?

I infer from what you say that the tokens in the ref could never be negative. However:

  • remaining is computed as available - weight;
  • if available < weight, then remaining is negative
  • a negative number is stored in the ref
  • at the next element, assuming not enough cycles have passed, tokens + (cycles * units) is still negative, so maxTokens is used
  • assuming that element is heavy enough, we still go into negative values for remaining;
  • the next round repeats the same, effectively not throttling.

WDYT?

@vasilmkd
Copy link
Contributor Author

Yes, tokens can become negative. However, the sink is sleeping for enough time required such that tokens amount of tokens can be accumulated, thus at the time when the next element is processed, there are at least 0 tokens.

@iravid
Copy link
Member

iravid commented Jul 21, 2019

Oh that's true! Thank you for pointing that out. So I'll read your comments about the burst implementation again and reply.

@vasilmkd
Copy link
Contributor Author

No problem. I was reading through the source code of akka-streams and the implementation of this is quite similar. It seems that they don't care about clock precision as much though.

@iravid
Copy link
Member

iravid commented Jul 21, 2019

@vasilmkd The current implementation (and the units + burst trick) looks good to me! It's a very elegant solution.

This is good to merge by me, unless there's anything else you wanted to address.

@vasilmkd
Copy link
Contributor Author

Happy with how everything works now.

@iravid iravid merged commit 58c4081 into zio:master Jul 21, 2019
@iravid
Copy link
Member

iravid commented Jul 21, 2019

Awesome work, thank you @vasilmkd!

@vasilmkd vasilmkd deleted the feature/throttle-burst branch July 21, 2019 19:40
ghostdogpr pushed a commit to ghostdogpr/scalaz-zio that referenced this pull request Jul 26, 2019
…1205)

* Change managed sink environment and error type

* Add burst control to throttleEnforceM

* Test ZSink.throttleEnforce with no burst

* Add burst control to ZSink.throttleShape

* Change the burst control API and default arguments for enforcing throttle

* Change burst control API and default argument value for shape throttle

* Tweak ZSink.throttleShape test to show lack of burst

* Add ZSink.throttleShape test with burst
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add throttling combinators to ZStream

2 participants