Debouncing async Tasks
Or collapsing requests, could be another way of saying.
This idea came to me from looking over the Netflix Hystrix documentation,
Hystrix, used to be the resiliency library netflix used for most microservices they built.
Hystrix
The idea around Hystrix is fairly simple.
Encapsulate all calls to external dependencies, in Commands.
These commands would contain logic for Circuit breakers, Timeout, Collapsing, Caching, Metrics, Bulkheading, Fallbacks and so on.
All of this could then be grouped so you could have a certain circuitbreaker be used for multiple different commands going to the same dependency to limit cascading failures. (The Hystrix circuitbreaker uses rolling window of failures).
Back to debouncing..
This got me to the idea of how to do “Debouncing” or in Hystrix terms “Collapsing” of multiple requests in .Net.
The idea behind it is, that if your request is Idempotent or otherwise should be debounced (note this is different from Throttling), you don’t need to call the dependency many times at the same time, as this could cause congestion.
If you look at the below image — mind you, this is for Java. The threading model of .Net differs a bit especially when we look at async/await.
To the code
There are a few points to make on, how to go about this.
— Which request is the one we fire off? Last or First during the time window?
— How do we keep track of the rolling time window?
Both a pretty simple actually, we simply keep track of the last time we executed + our time-window, and if we are within this time, the next time it is run, we return the lastResult
.
And a simple test
The test simply creates a Function that adds 1 to i
every time it is run.
When putting it through the Collapser
, within a timeframe of 100ms, it should run only once.
if you are to use the Collapser
, you should look at saving instances of it for other usage (I would probably just use a singleton CollapserFactory
that holds a ConcurrentDictionary<string, Collapser<T>>
where the string is a key, so you get to collapse across multiple threads (this is where it shines..)