Hacker News new | ask | show | jobs
by bryancoxwell 521 days ago
This works, but goes against convention in that (from the context package docs) you shouldn’t “store Contexts inside a struct type; instead, pass a Context explicitly to each function that needs it.”
2 comments

It does seem an unnecessarily limiting convention.

What will go wrong if one stores a Context in a struct?

I've done so for a specific use case, and did not notice any issues.

This guidance is actually super important, as contexts are expected to be modified in a code flow and apply to all functions that are downstream of your current call stack.

If you store contexts on your structs it’s very likely you won’t thread them correctly, leading to errors like database code not properly handling transactions.

Actually super fragile and you should avoid doing this as much as is possible. It’s never a good idea!

What do you mean by "won't thread them correctly"?

The specific use case I had was where the context represented the lifetime of a (shared) TCP connection. I then wanted to use its cancellation to drive the destruction of various dynamic graph elements hanging off that shared connection.

Think a graph of muxes/demuxes in a dynamic message graph while the whole program is a CSP style thing.

I needed something to drive destruction, the context provided what I needed.

> What will go wrong if one stores a Context in a struct?

Contexts are about the dynamic contour, i.e. the dynamic call stack. Storing the current context in a struct and then referring to it in some other dynamic … context … is going to lead to all sorts of pain: timeouts or deadlines which have already expired and/or values which are no longer pertinent.

While there are some limited circumstances in which it may be appropriate, in general it is a very strong code smell. Any code which passes a context should receive a context. And any code which may pass a context in the future should receive one now, to preserve API compatibility. So any exported function really should have a context as its first argument for forwards-compatibility.

Except Contexts provide a cancellation mechanism which can be used outwith of a specific dynamic call graph.

The documented semantics of the facility (and the implementation) seemed perfect for destroying a dynamic graph of CSP elements, which were exchanging messages. Where the cancellation causes the goroutines within the (think Actor-like) CSP element to clean up, tear down, and exit.

The warning about not storing it in a struct seems to assume it can only be used for one specific type of purpose, say web server processes and/or related database requests.

If I hadn't used it, I would have had to create something almost identical - but without the ability to store a data element.

True. But this code is only proof-of-concept of how non-context-aware functions can be wrapped in a context. Such usage of context is not standard.