Reputation: 13943
I'm new to Go lang and I could use some suggestions on how to refactor the code. All I got to do is depending on the success or error from Sarama (Apache Kafka thing in go) I need to log and forward it further. So far my code looks like this
go func() {
for err := range producer.Errors() {
batchID := err.Msg.Metadata.(ackMeta).batchID # notice the struct here
statusChan := err.Msg.Metadata.(ackMeta).statusChan
statusChan <- false
close(statusChan)
logs.Debug(appName, "Signalled failure on statusChan for batch ", batchID)
logs.Error(appName, "Failed to publish data to analyzer for batchID: ", batchID, err)
}
}()
go func() {
for succ := range producer.Successes() {
batchID := succ.Metadata.(ackMeta).batchID # notice the struct here
statusChan := succ.Metadata.(ackMeta).statusChan
statusChan <- true
close(statusChan)
logs.Debug(appName, "Signalled success on statusChan for batch ", batchID)
logs.Debug(appName, "Successfully published data to analyzer:", succ.Topic, succ.Key, succ.Partition, succ.Offset, succ.Metadata)
}
I think I can do a better job and wrap the whole thing in a single function but so far I can't think of any other apart from using the switch case as shown here
func checkSuccessOrFailAck(msg interface{}) {
switch msgType := msg.(type) {
case producer.Errors:
batchID := msg.Msg.Metadata.(ackMeta).batchID
statusChan := msg.Msg.Metadata.(ackMeta).statusChan
statusChan <- false
close(statusChan)
logs.Debug(appName, "Signalled failure on statusChan for batch ", batchID)
logs.Error(appName, "Failed to publish data to analyzer for batchID: ", batchID, msg)
case producer.Successes:
batchID := msg.Metadata.(ackMeta).batchID
statusChan := msg.Metadata.(ackMeta).statusChan
statusChan <- true
close(statusChan)
logs.Debug(appName, "Signalled success on statusChan for batch ", batchID)
logs.Debug(appName, "Successfully published data to analyzer:", succ.Topic, succ.Key, succ.Partition, succ.Offset, succ.Metadata)
}
}
The types of messages are different and so is the way I extract the attributes from it. But I'm not happy with this approach as the statements are more than the previous one. Could there be a better way to think to write ?
Upvotes: 0
Views: 49
Reputation: 46413
You could do them both in a single goroutine using a select
:
go func() {
errsOpen := true
succsOpen := true
for errsOpen && succsOpen {
select {
case err,errsOpen := <- producer.Errors()
batchID := err.Msg.Metadata.(ackMeta).batchID # notice the struct here
statusChan := err.Msg.Metadata.(ackMeta).statusChan
statusChan <- false
close(statusChan)
logs.Debug(appName, "Signalled failure on statusChan for batch ", batchID)
logs.Error(appName, "Failed to publish data to analyzer for batchID: ", batchID, err)
case succ,succsOpen := <- producer.Successes()
batchID := succ.Metadata.(ackMeta).batchID # notice the struct here
statusChan := succ.Metadata.(ackMeta).statusChan
statusChan <- true
close(statusChan)
logs.Debug(appName, "Signalled success on statusChan for batch ", batchID)
logs.Debug(appName, "Successfully published data to analyzer:", succ.Topic, succ.Key, succ.Partition, succ.Offset, succ.Metadata)
}
}
}()
This will loop until both channels have been closed, and at each loop iteration, handle a value from whichever channel has a value waiting to be received (or if both have values waiting, choose one at random).
Upvotes: 0
Reputation: 273366
First, it's not clear if the second code works at all. Since Errors()
and Successes()
return channels, you'd need to select
to read from both of them simultaneously without blocking either.
Since succ
and err
have different types in the code, I'm not sure you can do it much shorter than your original sample. Go doesn't have generics yet, so writing code that would unify the two is challenging. If you really need it I'd try to find the closest points where the type is the same - it should be easier to unify code from that point on. for example err.Msg
and succ
are the same type?
Moreover, even if Go had generics already, it's not clear unifying these would be a win. Go's philosophy is that a little code repetition is better than over-complicated abstractions. The code isn't the shortest it could be in theory, but it's explicit and clear.
Upvotes: 1