The Context Package

Photo by Walkator on Unsplash

The Context Package

A way to pass along context

Understanding it

The context package is used to pass information through a request scope and signals like cancel and deadline.

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

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

Request --> Server --> Function --> Database

If the request is canceled, 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 third-party libraries use 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 has a request scope, should 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:

|Handler| --context--> |Service| --context--> |Database|

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

If the database save is executing and the HTTP request is canceled, 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 a 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
}