
Suture – Supervisor Trees for Go - dataminer
http://www.jerf.org/iri/post/2930
======
jerf
A report, after using this library for more than a year and a half: First,
yes, I still use it in every program that is not a pure web server or pure
CLI. My Go programs definitely resemble my Erlang programs in that they are
ultimately trees of supervisors. Depth is about the same; I rarely go more
than three layers deep and the trees are quite breadth-oriented rather than
depth.

Second, I have found a lot of utility comes from having a standardized "this
is my set of services" object that can be manipulated. A lot of my Go programs
have converged on "a lot of libraries providing a lot of services, and a
rather grotty-looking ~100-200 line 'main.go' that does all the nasty ops-type
work of propagating configuration to them and plumbing them all together".
Most of those main.go's have a supervisor created near the top, various bits
of library functionality added to the supervisor as the file goes along, and
the last line is supervisor.Serve().

In terms of error handling, while I have seen it keep production services up
in a reasonable manner, Go code tends not to crash much, if you mind your
errors properly. The orchestration may actually be the nicer thing. I've
gotten a few issues in the github project and refined things a bit over the
past year; one of the use cases that came up is that shutting down the
services with one ".Stop()" call has been useful to people.

(We also got an interesting example of when penetrating an abstraction can
come in handy; supervisors take a couple of functions to allow you to log some
things. When a supervisor child is added to a supervisor parent, the log
functions are copied from parent to child by penetrating the Service with a
type assertion. This makes it easier to compose supervisors because the child
supervisors no longer have to guess at what logging you want.)

All in all, while I will re-emphasize strongly that it is _not_ a straight-up
replacement for all bits of Erlang functionality as I explain at some length
in the post, I have found it useful enough to be worth writing and using
myself. Whether it passes the bar for "worth importing as a dependency", well,
that's your call.

(Also, I'm guessing this came up again for the explanation of why imperative
languages generally don't have thread-killing capabilities?)

~~~
pramodliv1
Loved the article! I'm not familiar with Erlang, but I was able to follow
along and finally understood why OTP is usually distributed with Erlang.

If an Erlang process crashes when processing a message, and it is restarted,
is the message replayed automatically by the runtime, or provide some kind of
mechanism to recover the message as well?

~~~
jerf
Erlang messaging's contract is 0-or-1 delivery. Once a process has received a
message, it is in the recipients mailbox and no longer the runtime's problem.
Resulting failures can theoretically cascade across a couple of processes
tightly bound to that message's result, but handling that is Erlang's bread-
and-butter, so generally, a couple of processes will drop dead, then be
restarted. Some request will get lost but the system will keep going.

You can also do things like send a message across a node boundary to a node no
longer connected, which looks a lot like sending a message to a process that
crashes it. In the general case, you don't get an "error" message when that
happens or anything, you just get timeouts as if the remote node received it
but never acknowledged it. It's "just another failure", just another day at
the office, nothing unusual here.

------
johnmaguire2013
Past comments:
[https://news.ycombinator.com/item?id=8330835](https://news.ycombinator.com/item?id=8330835)

------
zzzcpan
In imperative languages you can safely deliver "asynchronous exceptions" only
if you write your code asynchronously, i.e. in a form of asynchronous events.
Event loops usually deliver OS signals into event-driven programs in exactly
this way. And it's always safe, because none of the event handlers can be
interrupted or run in parallel, the whole program is designed with that
assumption.

This is how asynchronous processes might look like:

    
    
       func procA (cb func(error)) (func(error)){
          var enter, something, leave func(error)
          var shared int
          enter = func (error err){
             ...
             shared ++
             ...
             waitForSomething (something) 
          }
          something = func (error err){
             if err != nil { 
                post (leave)
                return
             }
             ...
          }
          ...
          leave = func (error err){
             ...
             post (cb)
          }
          return enter
       }
    

It is also easy to preserve same guaranties and schedule such processes to
multiple cores. Although to do that in Go you would need to explicitly pass a
struct with all of the information about each process, its resources and its
environments.

------
buttershakes
Can you share more of the reasons for porting from Erlang to Go?

~~~
masklinn
From a previous posting:
[https://news.ycombinator.com/item?id=8332182](https://news.ycombinator.com/item?id=8332182)

------
ntonozzi
How do languages like C deal with interrupts? Is it possible to deal with
asynchronous exceptions if your handlers are reentrant?

I suppose this would still have issues dealing with unfinished work under a
mutex.

------
rmetzler
Would it be possible / desirable to use Suture to implement something like
Supervisord [1] in Go? This is something Docker image maintainers might want,
because it would eliminate the dependency on Python.

1: [http://supervisord.org/](http://supervisord.org/)

------
rcarmo
Nice to see this again! I caught one of the earlier postings and lost track of
it over time.

Are there any more examples available? Folk using it in production?

------
mentat
From 2014. Started reading and recognized it.

