Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for callback functions around worker process events #153

Merged
merged 18 commits into from
Oct 24, 2018

Conversation

michalwski
Copy link
Contributor

@michalwski michalwski commented Oct 9, 2018

This PR allows to set callback funs for following events in worker process:

  • on_init_started - initialization started (before module's init function is called
  • on_new_worker - when the module's init function returns with result {ok, State} or {ok, State, Timeout}
  • on_worker_dead - when worker process terminates

These callbacks can be used by worker_pool users in order to increase relevant metrics or add logging when needed.

@michalwski michalwski added the WIP label Oct 9, 2018
@michalwski
Copy link
Contributor Author

@elbrujohalcon when improving esl/MongooseIM to use worker_pool for every outgoing pool of connections (databases, external HTTP services) we come up with an idea of callbacks around some worker process events. Could you please let us know:

  1. What do you think about the idea.
  2. If the proposed approach is acceptable.

@michalwski michalwski force-pushed the rel-3.1-worker-events-callback branch from bdd5787 to 083c64c Compare October 10, 2018 07:35
@elbrujohalcon
Copy link
Member

Hi @michalwski. I like the concept in principle but I have a few concerns.

Crashes / Timeouts

As it is implemented now, callbacks my crash the process (we're not checking them for errors) and they can also block it (they are run sequentially within the wpool_process functions). I would like to have callback execution happening in an separate process.

Specs / Types

I would like to be more strict on what a callback function can be, for that I would prefer, if possible, to turn wpool_worker_callbacks (which I would rename wpool_process_callbacks) into a behavior with all callbacks in -optional_callbacks.

Multiplicity

It seems like you're only allowing one function per callback. In general, it might be nice to allow users to dynamically add/remove various functions to each callback.

Existing Functionality

Considering these previous items, my mind immediately jumps to Hey! Don't we have a generic way to implement callbacks anywhere? And I think, can't we implement this with gen_event instead of recreating that behavior. We could start a gen_event dispatcher for each pool and then provide helper functions for users to attach to them. It's fine to allow them to list the functions in the Options, too.

@michalwski
Copy link
Contributor Author

Thanks for you reply @elbrujohalcon.

The gen_event approach looks interesting, at first I totally forgot about it. When I think about it now, I'm still not fully convinced. I think the callbacks functionality may not be used very frequently so maybe there is no need to start the dispatcher for every pool but rather on demand, when a callback is really needed.

I agree with all other points, this PR was just an initial work to show the idea. I'll continue working on it.

@michalwski
Copy link
Contributor Author

After giving it more thought implementing these callbacks with gen_event may not be convenient for end users. They would have to implement their handlers (with all the gen_event callbacks). gen_event behavior is not commonly used so I'd rather hide it form the end user.

gen_event can still be used internally just to process the events in other process than the worker. There would be only one event_handler allowing to add/remove subscribers. These subscribers would implement the wpool_worker_callbacks behavior with optional callbacks.

What do you think about this @elbrujohalcon?

@elbrujohalcon
Copy link
Member

Yeah, that's fine. Defining a new behavior wpool_process_callbacks and implementing it with a gen_event underneath is fine with me.

Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good progress in general, but I think it needs some tweaking.

-spec add_callback_module(wpool:name(), module()) -> ok | {error, term()}.
add_callback_module(Pool, Module) ->
EventManager = event_manager_name(Pool),
ok = gen_event:call(EventManager, wpool_process_callbacks, {add_callback, Module}).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why instead of having a single event handler and adding callbacks to its list, don't we let gen_event take care of that and just add a new event handler per module?
This function would use gen_event:add_handler(EventManager, {wpool_process_callbacks, Module}, Module) and the one below gen_event:delete_handler(EventManager, {wpool_process_callbacks, Module}).


-spec handle_event({event(), [any()]}, state()) -> {ok, state()}.
handle_event({Event, Args}, Callbacks) ->
[call(Callback, Event, Args) || Callback <- Callbacks],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If instead of managing a list of callbacks here, we let gen_event take care of that, this module (and in particular this function and the one below) will be simplified.

, permanent
, brutal_kill
, worker
, [gen_event]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the right value for this element is dynamic

@@ -340,6 +364,8 @@ process_sup_name(Sup) ->
list_to_atom(?MODULE_STRING ++ [$-|atom_to_list(Sup)] ++ "-process-sup").
queue_manager_name(Sup) ->
list_to_atom(?MODULE_STRING ++ [$-|atom_to_list(Sup)] ++ "-queue-manager").
event_manager_name(Sup) ->
list_to_atom(?MODULE_STRING ++ [$-|atom_to_list(Sup)] ++ "-event-handler").
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be -event-manager or -event-dispatcher, this one is not a handler.

src/wpool_process_callbacks.erl Show resolved Hide resolved

-spec init([wpool:option()]) -> {ok, state()}.
init(WPoolOpts) ->
{ok, maybe_initial_callbacks(WPoolOpts)}.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indentation :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, if instead of managing a list of callbacks here, we let gen_event take care of that, we won't need to initialize the list of callbacks here. We should do it in wpool_pool:start_link(…), probably after initializing the pool itself.

-export([notify/3]).
-type state() :: ordset:ordset(module()).

-type event() :: on_init_start | on_new_worker | on_worker_dead.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This module will define -callbacks for these 3 event types, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, to preserve the style of other behaviors, I would call them handle_worker_init, handle_worker_creation and handle_worker_death (or similar handle_<something> names).

@@ -34,6 +34,7 @@ start_link(Parent, Name, Options) ->
init({Name, Options}) ->
Workers = proplists:get_value(workers, Options, 100),
Strategy = proplists:get_value(strategy, Options, {one_for_one, 5, 60}),
maybe_add_event_handler(Options),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If instead of managing a list of callbacks ourselves, we let gen_event take care of that, this won't be needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still needed in order to add the initial callbacks passed in opts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still needed in order to add the initial callbacks passed in opts.

@michalwski
Copy link
Contributor Author

Thanks for all the comments @elbrujohalcon. This PR was not ready yet but it's good to have a feedback as early as possible! I'll apply changes based on your review and let you know when I consider the work finished.

@michalwski michalwski force-pushed the rel-3.1-worker-events-callback branch from bf7562f to 37c7c38 Compare October 22, 2018 10:22
@michalwski michalwski closed this Oct 22, 2018
@michalwski michalwski reopened this Oct 22, 2018
@michalwski
Copy link
Contributor Author

Triggering travis.

@michalwski michalwski closed this Oct 22, 2018
@michalwski michalwski reopened this Oct 22, 2018
@michalwski michalwski force-pushed the rel-3.1-worker-events-callback branch from 0e4b456 to dafe599 Compare October 22, 2018 13:14
@michalwski michalwski closed this Oct 23, 2018
@michalwski michalwski reopened this Oct 23, 2018
@michalwski michalwski added finished and removed WIP labels Oct 23, 2018
@michalwski
Copy link
Contributor Author

@elbrujohalcon I consider this PR finished. Do you have any other comments?

@codecov-io
Copy link

codecov-io commented Oct 23, 2018

Codecov Report

Merging #153 into rel-3.1 will decrease coverage by 0.67%.
The diff coverage is 89.58%.

Impacted file tree graph

@@             Coverage Diff             @@
##           rel-3.1     #153      +/-   ##
===========================================
- Coverage    95.73%   95.06%   -0.68%     
===========================================
  Files            9       10       +1     
  Lines          399      446      +47     
===========================================
+ Hits           382      424      +42     
- Misses          17       22       +5
Impacted Files Coverage Δ
src/wpool_pool.erl 94.87% <100%> (+0.42%) ⬆️
src/wpool_process.erl 100% <100%> (ø) ⬆️
src/wpool_process_sup.erl 100% <100%> (ø) ⬆️
src/wpool_process_callbacks.erl 79.16% <79.16%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update e08b307...e6bd8a3. Read the comment docs.

Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks really really good. Awesome job, @michalwski !! 💯
Just a few minor tweaks and we're done!

README.md Show resolved Hide resolved
, handle_call/2
, handle_info/2
, code_change/3
, terminate/2]).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
, terminate/2]).
, terminate/2
]).

src/wpool_process_callbacks.erl Show resolved Hide resolved
undefined ->
ok;
EventMgr ->
[gen_event:add_handler(EventMgr, {wpool_process_callbacks, Module}, Module)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we don't care about the result of this list comprehension, why not using lists:foreach instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List comprehension feels more natural for me, that's why I used it here. There is no need to create a fun fro that, we can use the gen_event:add_handler directly here.
To optimize the code a bit I can return ok just after the list comprehension so the result will go to garbage sooner.
If you insist I can change to lists:foreach.

@michalwski
Copy link
Contributor Author

@elbrujohalcon I think all the comments/suggestions were addressed in the latest commit.

@michalwski michalwski force-pushed the rel-3.1-worker-events-callback branch from 30b1246 to 1451eea Compare October 24, 2018 08:43
Copy link
Member

@elbrujohalcon elbrujohalcon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just one final change, now that we moved to lists:foreach/2 (thanks for that, BTW)

src/wpool_process_sup.erl Outdated Show resolved Hide resolved
@elbrujohalcon
Copy link
Member

Note to self:

@elbrujohalcon don't forget to remove the optional callbacks from the gen_event implementation when replicating these changes on master.

@elbrujohalcon
Copy link
Member

@michalwski you might want to consider adding something like

-spec complete_coverage(config()) -> {comment, []}.
to appease @codecov-io 🤷‍♂️

Co-Authored-By: michalwski <michalwski@gmail.com>
@elbrujohalcon
Copy link
Member

elbrujohalcon commented Oct 24, 2018

@michalwski it's approved. GREAT WORK! Merge it when you feel like it and I'll take care of propagating this changes to master later :)

@michalwski
Copy link
Contributor Author

ou might want to consider adding something like ... to appease @codecov-io

I'm not sure about it. I'm fine with these 3 lines not covered. I can add it if you insist, though.

@elbrujohalcon
Copy link
Member

I don't insist, we have #98 and most of them will go away with the usage of optional callbacks when migrating to master anyway…

@michalwski
Copy link
Contributor Author

I'll merge it later today.

@michalwski
Copy link
Contributor Author

@elbrujohalcon thanks for all your reviews and good suggestions!

@michalwski michalwski merged commit e94a682 into rel-3.1 Oct 24, 2018
@elbrujohalcon elbrujohalcon deleted the rel-3.1-worker-events-callback branch October 5, 2021 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants