Prevent multiple simultaneous polling loops #208
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
The central mechanism of the
PollingBlockTracker
is thesynchronize
method which provides a loop to fetch the block number then wait for a given interval, all until the block tracker is no longer running meaning it no longer has any event listeners.If all listeners are removed while the
synchronize
loop is waiting for the timeout, once the timeout finishes the loop will exit sinceisRunning
will now befalse
.If all listeners are removed but then some are added again, all while waiting for a timeout, this allows a new synchronise loop to be created since
isRunning
wasfalse
meaning_maybeStart
>_start
>_synchronize
. Once the original timeout finishes however, it continues as normal sinceisRunning
istrue
again meaning we now have two synchronize loops occurring simultaneously with different interval alignment meaning we ultimately poll more often than the desired polling interval. This problem can stack and create many simultaneous loops thereby greatly increasing the actual polling interval.Test Steps
Solution
There are multiple possible solutions but for the sake of the simplest final code, this PR replaces the
synchronize
loop with asetTimeout
chain.This allows duplicate "loops" to be easily avoided since we can simply clear any pending timeouts before creating a new one, plus do the same when all listeners are removed and the
_end
method is ultimately called.In addition, we can simplify the
_maybeStart
,_maybeEnd
,_start
, and_end
methods by removing theasync
keyword since no asynchronous logic is required. This introduces additional clarity in code path since we are invoking these methods from synchronous event listeners meaning it is ideal that we only require synchronous methods.Note that
updateAndQueue
is anasync
method but we don't need toawait
this since we want to emit the_started
event before the first iteration has been completed, and it includes its own error handling.