logo

Lucas Katayama/The Context Package

Created Sun, 05 Dec 2021 10:38:19 -0300 Modified Sun, 05 Dec 2021 10:38:19 -0300
383 Words

Understanding it

The context package is used in request scope to carry values and signals like cancel and deadline.

My main use is with HTTP request is made and I need a way to cancel all the entire process if the user cancels the request.

For instance, if you have this process in your server:

graph LR;
    req(Request)-->srv(Server);
    srv-->func(Function);
    func-->db(Database);

And the request is cancelled, you need to cancel all the subsequent executions.

To do that, we use the context package.

func DoSomething(ctx context.Context, value string) {
    select {
        case <-ctx.Done():
        // release 
        default:
        // do something 
    }
}

Another interesting thing is third party libraries uses the context to retrieve and use information inside it.

For instance, newrelic grabs Transaction information to compute a request time and trace it.

Using it

It is pretty simple.

Every function call that hash a request scope, it must have the 1st argument as ctx context.Context.

func Save(ctx context.Context, name string) error {
	tx, err := dbConn.Begin()
	if err != nil {
		// handle
		return err
    }
	_, err := tx.ExecContext(ctx, 'INSERT INTO names(name) VALUES (?)', name)
	if err != nil {
		 return err
    }
	
	return nil
}


func Service(ctx context.Context, name string) error {
	return Save(ctx, name)
}

func Handler(w http.ResonseWriter, r *http.Request) {
	name := r.URL.Query("name")
	if err := Service(r.Context(), name); err != nil {
		// handle error
		return
    }
	w.Write([]byte(name))
	return
	
}

So it works like this:

graph LR;
    A(Handler) --context--> B(Service);
    B --context--> C(Database);

When a request is cancelled, all the signal is passed down.

If the database save is executing and the HTTP request is cancelled, a signal is passed and the ExecContext is stopped.

Cancellation

WithCancel

Returns a cancel function to signal a cancellation.

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

WithDeadline

Returns a cancel function to signal a cancellation or cancel it automatically at certain time.Time.

// Cancels after 5 seconds or when cancel is called
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
cancel()

WithTimeout

Returns a cancel function to signal a cancellation or cancel it automatically after a time.Duration.

// Cancels after 5 seconds or when cancel is called
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
cancel()

Context Values

Withvalue

Pass values through context:

ctx := context.Background()
ctx = context.WithValue(ctx, key, value)

Retrieve value

if value, ok := ctx.Value(key).(type); ok {
	// handle
}