chapter nine

9 Concurrency: Practice

 

This chapter covers

  • Preventing common mistakes regarding the main Go concurrency primitives: goroutines and channels
  • Understanding the impacts of using the standard data structures alongside concurrent code
  • Delving into the standard library and some extensions
  • Discussing subtle bugs that may lead to data races or even deadlock situations

In the previous chapter, we discussed the foundations regarding concurrency. Now, it’s time to delve into practice and concrete mistakes made by Go developers when working with the concurrency primitives.

9.1 #61 - Propagating an inappropriate context

Contexts are omnipresent when working with concurrency in Go, and in many situations, it may be recommended to propagate them. However, sometimes context propagation can lead to subtle bugs, preventing subfunctions from being correctly executed.

Let’s consider the following example. We expose an HTTP handler that performs some tasks and returns a response. However, just before returning the response, we want also to send it to a Kafka topic. As we don’t want to penalize the HTTP consumer latency-wise, we want the publish action to be handled asynchronously within a new goroutine. We assume that we have at our disposal a publish function, accepting a context so that the action of publishing a message can be interrupted if the context is canceled, for example.

Here would be a possible implementation:

9.2 #62 - Starting a goroutine without knowing when to stop it

9.3 #63 - Not being careful with goroutines and loop variables

9.4 #64 - Expecting a deterministic behavior using select and channels

9.5 #65 - Not using notification channels

9.6 #66 - Not using nil channels

9.7 #67 - Being puzzled about a channel size

9.8 #68 - Forgetting about possible side-effects with string formatting

9.8.1 etcd data race

9.8.2 Dead-lock

9.9 #69 - Creating data races with append

9.10 #70 - Using mutexes inaccurately with slices and maps

9.11 #71 - Misusing sync.WaitGroup

9.12 #72 - Forgetting about sync.Cond

9.13 #73 - Not using errgroup

9.14 #74 - Copying a sync type

9.15 Chapter summary