-
-
Notifications
You must be signed in to change notification settings - Fork 54
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
Implementing Erlang supervisor
-based dynamic supervisor
#87
base: main
Are you sure you want to change the base?
Conversation
…ild_with_args` function
…aves differently from the others, and one for the others.
…separate types and functions for the "simple" supervisors. Deprecating `static_supervisor`.
…rvisor(kind)` phantom type
Hello! The issue was to add dynamic-supervisor, but this seems to be flexible bindings to the Erlang supervisor module in general, but we have a slightly more specialised version of that already, It seems to me that this combined API is a lot more challenging to understand than the split design proposed and also used in Elixir. What do you see as the advantages in exchange for that drawback? Thank you |
There doesn't seem to be any meaningful difference between a "static" or "dynamic" supervisors in Erlang. Instead, it's the children that are defined statically or dynamically, but either way they're following the exact same child spec. That being said, I think it's possible to clarify the API while keeping that supervisor flexibility. I think it's fair to criticize the API. When I first built this I was mostly worried with expanding the feature set of the existing Erlang static supervisor. That being said, my main concern is to only have a single What do you think of having a |
I disagree! They are different actor abstractions that fulfil different tasks. The fact that the API is almost perfectly split into two different set of functions using phantom types and two different start functions is evidence of this. Very little in this module is shared between both concepts, and we have good evidence from Elixir that splitting them into separate things makes it much easier to understand. That way what it does is clearer, and we don't have to explain on each function or type which one it is relevant to.
I think it would be better to design optimal APIs for each rather than assume they will be the same. Worse-case-scenario it's a small amount of duplication, and we have no problem with duplication in Gleam. |
The phantom type on the Both "simple" and "classic" supervisors can have dynamic and static children. Also, it's not duplication I have a problem with, it's flexibility. Having separate types for "dynamic" and "static" supervisors does create the restriction I mentioned in my previous comment, that one wouldn't be able to both add children statically, and then add more children dynamically. There's always workarounds, but then that's not really making the library easier to use, which is the goal. Again, I agree that the API should be split, but not by having distinct supervisor types. However I also noticed something. The "static" children are added via the Something like this: type Builder(kind) {
...
}
pub fn start_link(builder: Builder(kind), ...) -> dynamic.Supervisor(kind) {...}
type Supervisor(kind) {
...
}
pub fn start_link(options: ...) -> Supervisor(kind) {...}
pub fn start_child(supervisor: Supervisor(kind), ...) -> Supervisor(kind) {...} There would still be more to do about the "simple" and "classic" supervisors but maybe this would split the API in a way that's acceptable to you ? One that still follows the Elixir example without adding unnecessary restrictions ? EDIT: |
Implementing #42
Following lpil' recommendation, I started from the
static_supervisor
module, which uses Erlang'ssupervisor
module. I added bindings to the following Erlangsupervisor
functions:start_child/2
terminate_child/2
restart_child/2
delete_child/2
count_children/1
I also added a new supervisor type,SimpleSupervisor
, which implements thesimple-one-for-one
strategy. It needed its own type, because it behaves significantly differently with the aforementioned functions than the other strategies. In my opinion, they are different kinds of supervisors altogether.As a result, the
Supervisor
can be managed with functions likeadd_child
,terminate_child
andcount_children
, while the equivalent functions for theSimpleSupervisor
are prefixed withsimple_
(simple_add_child
,simple_terminate_child
,simple_count_children
) to have them clearly stand out and stick together in the hex function index.I named the new module
erlang_supervisor
. Since my inspiration wasstatic_supervisor
, anyone using thestatic_supervisor
can use the newerlang_supervisor
instead. As a result, I took the initiative to deprecate all the functions in thestatic_supervisor
module (apologies if that's overreach on my part).Because of how different theSupervisor
andSimpleSupervisor
are, I think there's an argument to split them into separate modules. However they would be using the same Erlang bindings under the hood, so there'd have to be a third internal module for the bindings and other common logic. I wasn't sure how much should be in that internal module and how much should stay exposed, so I didn't split them.Finally, I updated all the tests of the existing strategies with the new functionalities, and wrote a new test for the
simple-one-for-one
strategy.I think some things could be improved, like the error types for the Erlang functions.
EDIT:
Since I first opened this PR, I learned about phantom types. Now, the
Supervisor
andBuilder
types are phantom types, to distinguish the "simple" supervisors from the "classic" ones. This is still necessary, since the "Simple" supervisor (which only implements thesimple-one-for-one
strategy) and the "Classic" supervisor (all 3 remaining strategies) have similar but not identical behaviors.I also changed the
supervisor_child
function to be built from aBuilder
while still returning aChildSpec
, making it easy to build a supervision tree, both when starting the whole tree in one go or when progressively adding supervisor children withstart_child
.