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.
Why
When we are about to execute a standalone function, we first need to load the module. This is a synchronous call to the Code server.
So far, we checked that the module was already loaded using
code:is_loaded/1
before doing that to avoid too many useless calls to the Code server.Unfortunately, up to Erlang/OTP 25,
code:is_loaded/1
is a synchronous call too! It is changed to an ETS query in Erlang/OTP 26, but before that, it remains an expansive and contentious call. If the same standalone function is executed concurrently a lot of time simultaneously, we end up spamming the Code server with a boat load of "is loaded" queries.What
To solve the problem, we introduce a
horus_utils:is_module_loaded/1
helper. On Erlang/OTP 26+, we callcode:is_loaded/1
. However on Erlang/OTP 25 and before, we replace that with a test oferlang:get_module_info/2
, the function underneath anyModule:module_info/1
calls.This way, it's a relatively cheap way of checking that the module is loaded.
At the same time, we acquire a lock around that module load code to make sure there is only a single attempt to load the module and concurrent processes will stop at "is loaded".