Parametrize the rate limiter with sleep-fn#71
Conversation
- extract the common sleep duration logic into the `->sleep-ms` fn - clearly separate millis from `nil` in code for fns return values - get rid of excessive `do-acquire` and `do-try-acquire` fns - move an actual sleeping into the `sleep` utility fn
to allow for having rollback semantics during an interruptible sleep
|
@marksto Thank you for the patch. Overall this looks good to me. I will take a look deep into the detail tomorrow when I find time. |
|
This looks good to me. I hope you can include some other candidates of |
|
@sunng87 Hi Ning!
Yes, I can add one, which endorses an uninterruptible sleep instead of a regular one. I will add this in the scope of the current PR in a few days, if you don't mind. There's no rush. =)
Yep, that was the point behind having this |
|
@marksto Sounds good. We can have:
|
After giving it some time I've figured out that this rollback feature does not belong to the That said, I've simplified the The only thing that is still missing are unit tests for the newly added uninterruptible |
and thus start affecting each other due to threads depletion (not good wrt isolation)
in order to exemplify a principal difference between the sleep semantics
with a single special case of uninterruptible sleep and interruption signal that won't stop any tasks from running (add a comment there, that's as well expected, yet we don't want to overcomplicate everything because of this specific case)
|
Hi @sunng87! I finally had time to wrap things up by providing a comprehensive test coverage that also illustrates the subtle difference in various sleep semantics that the lib now provides us with. Would be more than happy to have this PR merged if everything looks reasonably good for you. |
|
@marksto Thank you! The patch looks good to me. |
|
@sunng87 Would you be so kind to release a new version? |
|
Just uploaded 0.12.0. Let me know if there is any issue. Thank you for the patch! |
Hi @sunng87!
Here's a proposal for making the existing Rate Limiter implementation more malleable to changes, in particular to different sleep semantics. (This keeps the bucket’s tokens accounting logic intact, just improves the overall design a bit.)
The current
Thread/sleepis interruptible in a sense that anInterruptedExceptionis not given any special treatment, which may leave a rate limiter in an incorrect state wrt to the:reserved-tokensstate. While this may not be that critical for most applications (the state will be incorrect per se, but the calls will also not be executed, so the main rate limit invariant will be preserved), there are cases when the degradation of throughput is not desirable/acceptable — tight quotas, SLAs, multiple clients sharing the same limiter with a single quota, etc. In these cases it makes sense to have some sort of a rollback pattern in place (for instance, like in Bucket4j methods) so to keep the bucket’s token accounting correct.A trivial example of such rollback function would be:
which is to be called upon an
InterruptedExceptionduringThread/sleep. However, the existing design does not provide for such expansion, and I thought about how this could be added in the most unobtrusive way — an obvious candidate is to provide a new parameter, i.e.sleep-fn.Another approach to the problem would be to have sleeps uninterruptible. For example, the Bucket4j has such blocking strategy built-in. While this may help keep the rate limiter in a correct state, it has its obvious drawbacks and it also requires similar redesign (a parametrisation with
sleep-fn).I tried to make commits atomic and self-explanatory. But, please, note that this is nothing more than a suggestion for possible design improvements. I am open to discussing any alternatives and/or changes, as well as eventually providing some concrete
sleep-fnimplementations (this PR is a groundwork, after all).Cheers,
Mark