In golang we have the package context that can be passed along a request lifecycle to do cancelations. In elixir, what is the idiomatic way to do this?
If you model your actions as processes, aka the Elixir way, you can link those processes so that if one exits abnormally they all will.
As for timeouts, call/cast apiās (msg passing between processes) supports timeouts which will return with failure after that timeout.
is that what you are after?
After a quick scan of Go Concurrency Patterns: Context I figured:
- HTTP requests are typically handled in Elixir with Plug where the request/response is represented by Plug.Conn.
- Processing of the request is effectively ācanceledā with
Plug.halt/1
Though in the grander scheme of things that may just be a starting point ā¦ as already mentioned exits are signals in Erlang/Elixir - if a process has to clean up after itself, it needs to trap exits (Process.flag(:trap_exit, true)
) and if more than one process is involved there is typically a supervisor process.
So there is a whole range of options to serve scenarios of varying complexity.
Thanks! I think i got the way it need to be done, going to study more otp.
If a client disconnects when the request is being handled, is the plug pipeline halted?
Probably not, but the cowboy process, processing this connections, dies.
Wow, so it works the same as in Go, without having to do anything explicitly?
E.g. if the cowboy process exits, then if it was calling out to the database, is the database call automatically cancelled? And everything is cleaned up?
I donāt think itās automatic like this, but I am not particularly familiar with Ecto (a popular db wrapper in elixir). They might be trapping exits also, or they might be not. If they do, they might cancel the database call on the exit signal from the cowboy process that served the connection.
Doesnāt seem like they do [0].
But I donāt want to misinform you ā¦ Maybe someone from the ecto team can provide more insight on how it deals with ādanglingā database calls.
There actually was a question about it, but I donāt think anyone came up with a solution at the time [1].
[0] https://github.com/elixir-ecto/ecto/search?utf8=ā&q=trap_exit&type=
[1] Is it possible to stop query execution on db server when process that start to run that query killed?
Yes. If whatever checked out the connection crashes, the connection is cleaned up and put back in the pool.
@idi527 this is done by db_connection and the database adapters, thatās why you wonāt find it in Ectoās source.
Itās not same as context in Go though, it might be actually useful to have a similar convention.
What does Goās context solve that linking processes does not?
Desire to cancel a request is an orthogonal concern to something failing.
In terms of motivation you are correct and maybe Iām am misunderstanding what you are trying to say but exit signals arenāt necessarily limited to failures.
Itās simply a way to terminate a whole set of aggregated processes working towards some kind of common goal that has become unachievable or undesirable. So one of those processes receiving a ācancelā message and then proceeding to terminate all linked processes equates to the goal becoming undesirable - it isnāt considered a failure.
About the context package in Go and the similarities between it and Plug Conn, I wonder if it is a good or bad solution as described in this post:
https://faiface.github.io/post/context-should-go-away-go2/
What do you think?
From what I read they are entirely unrelated and are different concepts.
Plug.Conn
in Elixir is just the network socket and some associated data about the connection. It is not needed to cancel it, to do work, or anything else of the sort.
context
in Go seems like the network state at the time, it would be more akin to an Actor Process
in Elixir, not a plug connection. To ācancelā a context you have to tell it to do so. To ācancelā an Elixir Process you just, well, exit it or throw or whatever you want. The Process āisā the context in BEAM-land.
EDIT1: Also thanks for introducing me to āyet anotherā design quirk that I really dislike about Go. Further convincing me to not use the language as it just seems poorly designed every time I look at it. ^.^;
EDIT2: Even more reading shows that the goroutine ātreeā cancelable (in the form of context
) needs to be threaded-throughout, this is like the Supervision tree of OTP, which again is not like Plug.Conn
in any form, plus it ājust existsā in OTP by virtue of process links, not be some magical context that needs to be passed everywhere. Notice that this is the ādefault OTP modelā, unlike in Go where you have to explicitly and remember to pass contexts everywhere. That sounds horridā¦
EDIT3: And reading over Go Concurrency Patterns: Context - The Go Programming Language and context package - context - Go Packages just further reinforces that it is a very poor form of OTP. They are trying to emulate timeouts (built in to the BEAM), supervising subtrees (OTP Supervisors), marking things as canceled (which still run to completion unless explicitly checked, where in OTP everything that depends on something that was canceled just dies outright and immediately due process links), and more. This whole Context thing in Go seems like an absolutely horrid designā¦ but that seems to go with what Iāve seen of āGo the Languageā, so ehā¦
Some things that seems very similar to me is the use of Plug.Conn
as a bag to carry values accross the stack through use of the assign
function, which seems a very pragmatic decision, but sometimes this could grow as a kind of global variable with a lot of thinks in there.
Only values that relate to the state of the connection. The Plug.Conn should never even really escape your Controllers, if it does you are doing āSomething Wrongā. ^.^;
Which is quite the opposite of Goās context, which is āInfect The Everything!ā. ^.^
Sure, Thank you for the explanation.