elixir-lang / elixir

Elixir is a dynamic, functional language for building scalable and maintainable applications
https://elixir-lang.org/
Apache License 2.0
24.54k stars 3.38k forks source link

Migrate Logger away from GenEvent #5255

Closed josevalim closed 8 years ago

josevalim commented 8 years ago

The goal is to migrate Logger handlers away from GenEvent. We can either use Erlang's gen_event or have a custom implementation on top of GenServer. We are not going to use GenStage because this is one of the few cases where gen_event's architecture actually makes sense: we want to avoid copying between multiple handlers. Therefore, differently from #5254, this issue can be tackled now-ish.

ericentin commented 8 years ago

@josevalim I'd like to take a crack at this unless you have someone else in mind?

josevalim commented 8 years ago

@antipax that would be fantastic, please go ahead. I would start with gen_event at first. Keep in mind ExUnit kinda depends on Logger internals.

ericentin commented 8 years ago

So here's a couple things that have come up that we should discuss:

A possible solution to the first is to allow duplicate backends to be installed, or, we could track them separately somewhere, like an ETS table or another process.

As for the second, I think we could use add_sup_handler, because currently the process which uses add_mon_handler exits in all the cases it would with add_sup_handler, AFAIK.

josevalim commented 8 years ago

Yes, we don't need the mon handler feature because we can use the sup handler.

For the uniqueness thing though, maybe we can use the logger watcher supervisor to give us uniqueness, as it requires the watchers id to be unique. This means we can't use a simple one for one though (which is fine, we wouldn't have dozens of children anyway).

José Valimwww.plataformatec.com.br http://www.plataformatec.com.br/Founder and Director of R&D

ericentin commented 8 years ago

So, I don't think we can use a :one_for_one supervisor for this. The child spec for a :transient child persists in the supervisor even after the process dies, so you cannot remove and then re-add a child with a given ID. Any thoughts? Could just use a separate gatekeeper process or ETS table.

josevalim commented 8 years ago

I can think of two options:

/cc @fishcakez

fishcakez commented 8 years ago

Can we pass this to the calling process? The watcher can be removed with delete_child if it is present and keeps the uniqueness guarantee in a single place.

def start_backend(module, arg) do
  case Supervisor.start_child(WatcherSup, worker(Watcher, [module, arg], [id: module])) do
    {:error, :already_present} ->
      _ = Supervisor.delete_child(WatcherSup, module)
      start_backend(module, arg)
   other ->
    other
end
ericentin commented 8 years ago

Yes, that's a good point. I had not thought to use :already_present vs. :already_started for this purpose.

Could there be a (perhaps unimportant) race where two processes attempt to add a backend, and instead of it getting added once, it gets added, removed, and added again?

fishcakez commented 8 years ago

Its not possible because delete_child does not work unless the child has undefined pid. So the second client in the race will get an already started error.

On Tuesday, 27 September 2016, Eric Entin notifications@github.com wrote:

Yes, that's a good point. I had not thought to use :already_present vs. :already_started for this purpose.

Could there be a (perhaps unimportant) race where two processes attempt to add a backend, and instead of it getting added once, it gets added, removed, and added again?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/elixir-lang/elixir/issues/5255#issuecomment-249914625, or mute the thread https://github.com/notifications/unsubscribe-auth/AB6JTUh0SscKxJ9Nx09A6Bt7HinvyGIIks5quUCRgaJpZM4KGS_W .

ericentin commented 8 years ago

Perfect! Thanks @fishcakez! ❤️

I'll move ahead with this approach.