I am working on a client library that starts a Kafka consumer to listen for messages from a kafka stream and run some user defined handlers.
I want the design to be "crash-only" as much as possible, so will be avoiding cleanups during unexpected shutdowns.
For example, here's a simple sketch:
func myHandler(m client.Message) error{
...
}
c := client.New(...)
c.AddHandler(myHandler)
c.Start()
c.Start() looks like this:
func (c *Client) Start(){
for{
select{
case m := <-c.message:
for _, handler := range c.handlers{
err := handler(m)
}
}
}
}
If the handler encounters an error, I have the following options:
- call log.Fatal() within the for _, handler := range loop. This seems to be the simplest, but it doesn't seem right for a library to exit.
- remove the return error from the handler, and call log.Fatal() within so that the handler terminates the whole program.
- create an error channel, have the for _, handler := range loop write errors to that channel. In main(), have a for, select loop to read from the error channel and call log.Fatal() there.
Which of these options is the most idiomatic?
Cheers,
Francis