Quick tour : Go Context. A quick dip to the context package for…
By: Vishesh Jha
Tags: Context, First Post, Learning, Golang, Go

Go’s context package and feature is a core part of how most Go applications are built today. The Context package is extremely useful and probably one of the most versatile packages of the entire language. It is highly unlikely that you are here and you haven’t come across context. If you haven’t come across anything dealing with contexts yet, you probably will very soon .

Before we move forward, here is the official documentation for the context package https://golang.org/pkg/context/. It’s really great and filled with practical examples. Do check it out, if you haven’t.

Prerequisites

There are 2 concepts that you need some familiarity with to understand context which is goroutine and channels.If you have no familiarity with the above topics, do check out the official documentation for them and then continue with the article.

Context

It is no brainer to derive the functionalities of the package from its name. A simple way to think about the context package in go is that it allows you to pass in a “context” to your program. Very often, you want to define a timeout, deadline or have the ability to cancel a certain operation and all the invoked functionality from running respectively. When building applications for the web, all of those decisions are usually made inside of a request-response cycle.

Getting started

Package context defines the Context type, which carries deadlines, cancellation signals, and other request-scoped values across API boundaries and between processes. Let’s check them one by one.

context. Background() ctx Context

This function is usually used in the top level request handler. It is used to derive other contexts(we will discuss about this later) and has an empty context as a return type.

ctx, cancel:= context.Background()

context.TODO() ctx Context

The TODO function is used when you are not sure what context to use or if the function has not been updated to receive a context. There can be cases where you plan to add context to the function in the future, but you want things to remain smooth at the moment.

ctx, cancel := context.TODO()

You must be wondering how it is different from the context.Background(), right?

The difference is, TODO can be used by static analysis tools to validate if the context is being passed around properly, which is an important detail as the static analysis tools can help surface potential bugs early on and can be hooked up in a CI/CD pipeline.

context.WithValue(parent Context, key, val interface)

One of the most common uses for contexts is to share data or use request scoped values. When you have multiple functions and you want to share data between them, you can do so using contexts. The easiest way to do that is context.WithValue. This function creates a new context based on a parent context and adds a value to a given key. You can think about the internal implementation as if the context contained a map inside of it, so you can add and retrieve values by key. It allows you to store any type of data inside the context. It is not recommended to pass in critical parameters using context value, instead, functions should accept those values in the signature making it explicit.

ctx := context.WithValue(context.Background(), key, “test”)

context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)

This function creates a new context derived from the parent context. This parent function is passed in as argument . This parent context can either be a background context or a context that was passed into the function.

If you look at its return type , it returns a derived context and cancel function.

ctx, cancel := context.WithCancel(context.Background())

context.WithDeadline(parent Context, d time.Time) (ctx Context, cancel CancelFunc)

As the function name suggests this function returns a derived context from its parent which gets cancelled with fulfilment of either of the two conditions which are when the deadline exceeds or the cancel function is called.

When that context gets canceled because of the deadline running out, all the functions that got the context get notified to stop work and return.

ctx, cancel := context.WithDeadline(context.Background(),time.Now().Add(2 * time.Second))

context.WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)

Similar to WithDeadline , it cancels the contexts but this cancellation is based upon the time duration . This function returns a derived context that gets canceled if the cancel function is called or the timeout duration is exceeded.

ctx, cancel := context.WithTimeout(context.Background(), time.Duration(150)*time.Millisecond)

Good practices to keep in mind

As mentioned above, context.Background should be used only as the root of all derived contexts. It is advised that context.TODO should be used where you are not sure what to use or if the current function will be updated to use context in the future. One more good practice related to TODO is to never pass nil context , instead, use of TODO is advised . As mentioned above, context.Value should be used very rarely, it should never be used to pass in optional parameters. This makes the API implicit and can introduce bugs. Instead, such values should be passed in as arguments. Storing context in a struct is not advised, context should be passed explicitly in functions, preferably, as the first argument.

Final Thoughts

Contexts are used as part of Go basic features. Thus, it is very important to understand and know how to work with them. The context package provides a very easy and lightweight API to interact with this key component. Also, we discussed how single context could be used to control and carry cancellation signals as well as to carry scoped values. This makes the context a very important and powerful tool to create reliable and simple code.

I hope this helps. Happy Learning!