Golang Context Package

Golang Context 101

Hello, Gophers ! Today we will learn some important things:

  • What is a Golang context value
  • How to create values
  • How to add context to a function
  • How it helps your HTTP server be more efficient

so strap in and let’s start.

What is a Golang context package?

Golang context package defines the Context type, which carries deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes. Incoming requests to a server should create a Context, and outgoing calls to servers should accept a Context. The chain of function calls between them must propagate the Context. Two important contexts here are Cancellation & Propagation.

This means that you can request me to buy you a sandwich (for which you’ll pay extra), and I go to the market to buy it. Cancellation is the ability that if you call me midway and tell me that you don’t need a sandwich anymore, I would be able to stop no matter how close/ far away from the market I am.

Now consider the scenario that I tell you to make me a sandwich for teaching you Golang. And then you order five of your friends to go buy materials for the sandwich. Now if I tell you to cancel it, you should not only be able to stop making it, but you should also be able to call up your friends and tell them to drop the idea. This is called Propagation.

So, now coming back from our analogy of sandwiches to servers, context gives you the ability to timeout your server when a certain time has passed, reducing loads and resource usage.

How to create values

So, how do we do it? Open up your code editors. Today, I’m using VS-Code, because it’s awesome. It imports your packages for you automatically.

So let’s import the necessary packages:

package main

import (
	"context"
	"fmt"
	"log"
	"time"
)

The root method in the context package is called Background. It is like the root of your context tree.(And yes… we ARE making a tree – a context tree)

All other context methods are children of the root. To define a background, we use :

c := context.Background()

We can add this context to any function using context.Context. So say we have a function myfunc, then we can add the context as an argument:

func myfunc(c context.Context, arg string) string {
//do something for context
     time.Sleep(2*time.Second)
     fmt.Println("Hello %s",arg)
}

We have four ways in which we can make our context stop the program execution if a lot of time has passed:

  • context.Background
  • context.WithCancel
  • context.WithTimeout
  • context.WithDeadline

Adding context to a function

Well, now that we know how to define a context value, we must pass it in a function. For example, if you try to go run this:

package main

import (
	"context"
	"fmt"
	"log"
	"time"
)

func main() {
	c := context.Background()
	c, cancel := context.WithTimeout(c, time.Second)
        defer cancel()
        myfunc(c,"Gopher")
        }

func myfunc(c context.Context, arg string) string {
//do something for context
     time.Sleep(2*time.Second)
     fmt.Println("Hello %s",arg)
}

it will print Hello Gopher, ignoring the context. So we have two different channel operations that we want to perform at the same time, and for that, we can use select.

We need to add the function at the same time as time.Sleep, and the time package provides us with a handy way to do that:

package main

import (
	"context"
	"fmt"
	"log"
	"time"
)

func main() {
	c := context.Background()
	c, cancel := context.WithTimeout(c, time.Second)
        defer cancel()
        myfunc(c,time.Second,"Gopher")
        }

func myfunc(c context.Context,d time.Duration, arg string) string {
     select {
     case <- time.After(d):
           fmt.Println("Hello %s",arg)
     case <- c.Done():
           log.Print(c.Err())
     }
}

time.After(d) will wait d units of time, and then print Hello Gopher. BUT if the context times out within that time, then it will print

Context Deadline Exceeded
Context Deadline Exceeded

Improving Efficiency and Minimizing Waste

If you just do that on every single HTTP handler – if in every HTTP handler you just do that, of either – like, “I’m gonna call this function, but also if this happens, just cancel it”, if the user just sends a request and cancels the request, because the TCP connection goes down, that context will be canceled. So valuable resources like bandwidth and storage can be allocated elsewhere.

References