Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

proposal: context: functional interfaces #62017

Open
eccles opened this issue Aug 14, 2023 · 3 comments
Open

proposal: context: functional interfaces #62017

eccles opened this issue Aug 14, 2023 · 3 comments
Labels
Milestone

Comments

@eccles
Copy link

eccles commented Aug 14, 2023

Description

The context.Context fulfils 3 different purposes:

  • bag of values
  • deadline
  • cancellation.

These different functionalities are not apparent in any API - one has to rely on documentation or read the source code to find out which of the different functionalities is expeceted from the user of any specific API.

I proppose that we take a leaf out of the io.Reader, io.ReadWriter ... interfaces in the io package and define:

  • context.Canceller
  • context.Values
  • context.Deadline
  • context.DeadLineCanceller
  • context.ValuesCanceller
  • context.ValuesDeadline
  • context.Context - all of them for backwards compatibility

API authors can then specify the correct context and this will document in the code the expected context behaviour.

Cons:

  • Some packages will parse the context and act accordingly and this scheme will not work for them without some modification. For example the azure-sdk-for-go will enact a Timeout if the passed in context has a Timeout attribute - otherwise it reverts to an internal retry with backoff.
  • adopting this for any API will probably cause a major version change.

From experience

Coding guidelines can dictate that a context contain values for general use such as headers or grpc metadayta. This leads to the decision to mandate that every network call uses a common context without realising that this common context might also contain a deadline which on every call becomes shorter and shorter leading to general flakiness. Fixing this in a large codebase without knowing what is expected of any passed in context is not trivial perhaps.

Hence the recent introduction of context.WithoutCancel().

I would assert that if we had the above subcontexts then WithoutCaancel may not be required.

@eccles eccles changed the title proposal: affected/package: context: functional interfaces proposal: context: functional interfaces Aug 14, 2023
@gopherbot gopherbot added this to the Proposal milestone Aug 14, 2023
@apparentlymart
Copy link

apparentlymart commented Aug 14, 2023

Hi @eccles,

The context.Context mechanism's primary mission is to allow cross-cutting concerns to travel across API boundaries without the entire application needing to agree on what those cross-cutting concerns are.

For example, in my own application I might have decided to use a particular distributed tracing instrumentation API, but it's unlikely that third-party libraries I'm using will have made the same decision. If I'm using a function in that third-party library to indirectly call a function in my own application -- a typical example being stdlib net/http.Server/Client calling my own application's http.Handler or httptrace.ClientTrace -- I cannot modify net/http's API to specify that it should expect and pass downstream a context.Values specifically.

With that said, I feel unsure about how the more specific interfaces you described here would be used. You say "API authors can then specify the correct context", but you've not described how the API author would decide which type to use.

If you are imagining using this only for calls between functions in a single codebase then indeed for that case I could imagine how that might work, but that situation is not the scenario that the context package API is intended for.

Of course that doesn't mean that the scope of context couldn't grow to better support that situation, but if that is your intention then I'd suggest including some concrete examples of how you'd use this in real code so that the value proposition is easier to see.

@ianlancetaylor
Copy link
Contributor

CC @Sajmani

@eccles
Copy link
Author

eccles commented Aug 15, 2023

Thank you @apparentlymart for your comments.

I am no proposing that we get rid of the generic context.Context. Just simply making it clearer for any particular API on how that AP is expected to be used with regards to the required context.Context argument.

So for example calling a listener of some kind (httpserver or similar):

  func (s *service) Listen(ctx context.Cancel, handle Handler)

which indicates that this function could be cancelled

or

   func (c *Client) Send(ctx context.ValuesDeadline, msg []byte)

which indicates that a timeout should be specified in the context and the Values will be used (maybe in the headers...)

or

    func (c *Client) Send( ctx context.Deadline, msg []byte)

which indicates that only a deadline will be expected in the context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Incoming
Development

No branches or pull requests

4 participants