[
https://issues.apache.org/jira/browse/CAMEL-9355?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15029166#comment-15029166
]
Aaron Whiteside commented on CAMEL-9355:
----------------------------------------
The problem we are seeing with the current throttler, is that with a single
thread we are never quite able to reach the actual throttle limit, and with
multiple threads in a route we end up going over the throttle limit. For our
business use-case this is disastrous, we're not able to meeting any of our
SLAs.
With the semaphore based implementation requests are not actually spread evenly
over the time period, as you can consume say a throttle rate of 100/s in the
first 1ms then be blocked for the remaining 999ms.
Another benefit of the semaphore implementation is that it's not based on a
relative point of time reference. For example: the senders time slot starts at
6 seconds past the minute, vs the receivers time slot starts at 4 seconds past
the minute- assuming the clocks are synchronized. This can lead to the receiver
rejecting requests because they exceed his throttle, when from the senders
point of view he has not exceeded the throttle.
Of course this depends on how the receiver calculates received throughput. But
it is always better to use a rolling window of the last "time period" (Eg. 1s),
relative to the current time/current request. It may not be obvious at first
but the semaphore is a rolling window of time.
When using a semaphore it's not really possible to have the permit be released
without using another thread. There are other approaches but they suffer from
the problem of assuming the time period window starts at some fixed time
relative to them and have the issues mentioned above, or have very high
overheads.
Such an approach, that was not a semaphore, we were using a few years ago was a
throttling implementation based on a sentinel (head and tail are the same
object) linked list. Where we would add items to this queue with a timestamp
and then when checking if we exceeded the throttle, we would count back through
the items until we exceeded our throttle or reached a timestamp outside our
time period (again relative to the current time of the request). This achieved
the goal but was quite expensive, as we could end up looping over
o(throttle_rate) items, per requesting thread, to see if we were within our
throttle rate. With a high throttle rate and high concurrency this became a
bottle neck. Not to mention that you cannot just let the list grow unchecked,
it had to be pruned either with a dedicated thread or delegating the work to
each calling thread, slowing them down even more.
If this change is too radical, would you consider a throttler2 implementation,
living along side the original throttler?
> Current Throttler implementation is not accurate and does not work in a
> multi-threaded route
> --------------------------------------------------------------------------------------------
>
> Key: CAMEL-9355
> URL: https://issues.apache.org/jira/browse/CAMEL-9355
> Project: Camel
> Issue Type: Improvement
> Components: camel-core
> Affects Versions: 2.16.0
> Reporter: Aaron Whiteside
> Attachments: CAMEL_9355.patch
>
>
> Current Throttler implementation is not accurate and is even more inaccurate
> in a multi-threaded route (think sjms....consumerCount=100).
> The delay to sleep cannot be calculated ahead of time in a multi-threaded
> environment, to this end the Throttler should not extend
> DelayProcessorSupport.
> Attached is a patch that changes throttler to use a Semaphore to do accurate
> and multi-thread safe throttling.
> The code I think is much cleaner, smaller and easier to understand. Than it
> used to be before.
> Unit tests still pass, I had to make some changes to ThrottlerTest as it made
> assumptions about the implementation and was doing bad things like adding a
> 750ms buffer to validating the minimum throttle delay.. ThrottlerTest is now
> very sane.
> I've also implemented support to allow the throttler construct to be used
> without any nested outputs. For example the follow code is now valid.
> {code}
> <throttle><constant>100</constant></throttle>
> {code}
> If you want to disable this feature it can be done in
> ThrottlerDefinition::createProcessor() line 82 changing false to true.
> I think this allows more flexible usage of the throttler, in my use case I
> want to delay the further execution of the route, and I don't want to have to
> split my routes up into separate sub-routes to be able to do that.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)