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:
context.Context
for my solution? If not, give me a light on another approach.context.Context
?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.
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.