5

I have a web application written in Go with multiple modules, one deals with all database related things, one deals with reports, one consists all web services, one for just business logic and data integrity validation and several others. So, I have numerous methods, functions have been covered by these modules.

Now, the requirement is to use session in web service as well as we need to use transaction in some APIs. The first approach came to my mind is to change the signature of the existing methods to support session, transaction (*sql.Tx) (which is a painful task, but have to do in anyways!). Now, I'm afraid actually what if something will come in future that needs to be passed through all these methods and then should I have to go through this cycle again to change the method signature again? This does not seem to be a good approach.

Later, I found that context.Context might be a good approach (well, you can suggest other approaches too, apart from this!) that for every method call, just pass context parameter at first argument place in a method call hence I've to change methods signature only one time. If I go with this approach, can anyone tell me how would I set/pass multiple keys (session, sql.Tx) in that context object? (AFAIK, context.Context provides WithValue method, but can I use it for multiple keys? How would I set a key in the nested function call, is that even possible?)

Actually, this question has two questions:

  1. Should I consider context.Context for my solution? If not, give me a light on another approach.
  2. How do I set multiple keys and values in context.Context?
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
rsudip90
  • 799
  • 1
  • 7
  • 24
  • 1
    You set multiple values, by setting multiple values. Can you show the problem you were having when doing that? You could also just set a single value that contains all the data in one struct. – JimB Dec 15 '17 at 14:07
  • Again, I'm not having deep down experience in context thing, as per what I have seen is, programmer returns the context with value like `ip` or `userkey`. Thing is, if I want to use sql.Tx as well as session object in context, how would I achieve that so that nested functions can extract and use that info from the context? – rsudip90 Dec 15 '17 at 14:10
  • 3
    See related question [Design of Context interface](https://stackoverflow.com/questions/40654605/design-of-context-interface/40659449#40659449); and possible duplicate [Golang context.WithValue: how to add several key-value pairs](https://stackoverflow.com/questions/40379960/golang-context-withvalue-how-to-add-several-key-value-pairs). – icza Dec 15 '17 at 14:14
  • Thanks, @icza for sharing the links. – rsudip90 Dec 15 '17 at 14:38
  • I'd advise caution when using value contexts. There are numerous potential problems, not least of which are the loss of type-safety and the loss of transparency: by accepting a Context parameter, a function or method exposes zero information about what its requirements actually are. There are many posts on the issues with Context, I [found this one to be a good summary](https://faiface.github.io/post/context-should-go-away-go2/). – Adrian Dec 15 '17 at 19:35
  • Thanks for your note @Adrian. – rsudip90 Dec 16 '17 at 06:30

2 Answers2

3

For your second question you can group all your key/values in struct as follows:

type vars struct {
    lock    sync.Mutex
    db      *sql.DB
}

Then you can add this struct in context:

ctx := context.WithValue(context.Background(), "values", vars{lock: mylock, db: mydb})

And you can retrieve it:

ctxVars, ok := r.Context().Value("values").(vars)
if !ok {
    log.Println(err)
    return err
}
db := ctxVars.db
lock := ctxVars.lock

I hope it helps you.

omotto
  • 1,721
  • 19
  • 20
0

Finally, I decided to go with context package solution, after studying the articles from the Go context experience reports. And especially I found Dave Cheney's article helpful.

Well, I can make my custom solution for context as gorilla (Ah, somewhat!). But as Go already have a solution for this, I would go with context package.

Right now, I only need session and database transaction in each method to support transaction if began and user authentication, authorization.

It might be overhead, of having context.Context in each method of the application cause I don't need cancellation, deadline, timeout functionality at the moment but it could be helpful in future.

rsudip90
  • 799
  • 1
  • 7
  • 24