Go Context for a layman
Introduction
The context is a critical part of the whole Go ecosystem. In this brief article, I am going to explain how context works in a Go application. I will not enter into too many details of it but I will leave you this link for more information.
High-Level Overview
From a very high-level perspective, a Context can be compared to an n-ary tree data structure. Check this diagram below:
In this article, I will only take into account Go context WithCancel, which seems quite promising.
Context With Cancel
When a cancel signal to a context is emitted all the goroutines listening to the context are interrupted.
If the parent context has emitted the cancel signal, then all the sub-contexts derived from the parent context also receive the cancel signal and thus all goroutines listening on the context/s (parent & sub-context) are also interrupted.
Okay. Let's check the use cases.
Scenario 1
What is happening in the above diagram?
Goroutines 1,2 and 3 are using the same context.
Goroutines 2 & 3 are listening for any cancellation signal in the context using <-ctx.Done channel.
Goroutine 1 has emitted the cancel signal and immediately goroutines 2 & 3 are interrupted.
Scenario 2
Now, let's check a different scenario.
Goroutine 1 is working in a different context from the goroutines 2 & 3.
The context for the goroutines 2 & 3 are not derived from the context of goroutine 1.
Now, if goroutine 1 emits a cancellation event, will goroutines 2 & 3 be interrupted?
The answer is NO.
Scenario 3
Goroutine 1 is working in a different context from the goroutines 2 & 3.
The context for the goroutines 2 & 3 are DERIVED from the context of goroutine 1.
Now, if goroutine 1 emits a cancellation event, will goroutines 2 & 3 be interrupted?
The answer is hell YES.
Scenario 4
What will happen if the cancellation event is emitted for the root context?
Well, in that case, all the goroutines will be interrupted. It's awesome, right? I mean damn awesome.
Show me some code
Definitely. Below is my code logic in a pictorial view.
I have two stages, stage 1 & stage 2.
I have created ctxCancelStage1 for stage 1 derived from the root context.
Now, I have created another context ctxCancelStage2 derived from ctxCancelStage1 for stage 2.
Stage 1 is producing some data on channel data and stage 2 is reading data from the same channel.
Stage 1 is emitting a cancel signal for ctxCancelStage1 and consequently, the goroutine (worker) in stage 2 is listening to the event and processing accordingly. In the demo code, it's only exiting the loop to stop processing data.
Check the Github code:
On execution of the code, you should see the below output.
Now, let's tweak the code a bit.
Here, I have created the ctxCancelStage2 derived from the root context. Stage 2 is working with this context.
Triggered the cancel event on ctxCancelStage1.
Will Stage 2 be interrupted?
The answer is NO. In the demo code, the worker in stage 2 continues in the infinite for loop. (Memory leak).
Below is the result.
Conclusion
In this brief article, I have explained the use case of Context in Go. I find it extremely useful while working for concurrent request processing. Hope this article has helped anyone naive like me to figure out how context works.
Happy Go learning!