Is it safe to use sync.Pool reference as context value? - http

I have struct that uses sync.Pool.
Is it safe to use this reference as context value?
type User struct {
ID string
}
var userPool = sync.Pool{
New: func() interface{} {
return &User{}
},
}
func getUser() *User {
return userPool.Get().(*User)
}
func recycleUser(user *User) {
userPool.Put(user)
}
The user struct is retrieved from pool in middleware.
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// get user from pool
user := getUser()
// user must be recycled
ctx := context.WithValue(r.Context(), "user", user)
}
}
And recycled in handler.
func getUser(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value("user").(*User)
// TODO: do something with user
// put user struct back into pool
recycleUser(user)
}
Edit:
My question is more about how context deals with pointer to my object. Does it make a copy? Is it safe to use non-primitive object with context?

There are three points to make:
Usage of sync.Pool
Your usage of pool is as intended. From golang Documentation
A Pool is safe for use by multiple goroutines simultaneously.
Retrieving users
getUser uses func (p *Pool) Get() interface{} which removes the returned item from the pool, afterwards you can do whatever you please
with that value. In your case, thats a *User. Whether the associated
ressource is safe to use, well, depends on your usage of those values in the rest of the program.
Recycling users
Calling recycleUser within the hander is potentially dangerous, depending on your environment.
What could possibly go wrong?
After recycleUser returned your *User back to the pool, it could be retrieved and used by a different goroutine immediately. But at the same time, *User is still stored in the context assiociated with the request. So it depends if any of the functions of your middleware use the *User from the context, too and wether they store the *User value. Or if you later add some code after the recycleUser, which uses the *User value. After the recycleUser call, all those usages probably operate on the wrong user, which is already used by some different request.
How to solve those issues
coding convention
if you retrieve the user in middleware functions, don't store it
if you retrieve the user in middleware functions, don't use it after the call to the next handler in the chain.
don't call recycleUser in middleware functions
retrieve the user in the leaf handler (last handler in the chain, doesn't calls any further handlers propagating the context and thus the *User) and use defer recycleUser(user) to put back the *User in the pool and make it impossible that later added code uses *User after the call to recycleUser(user).
in the leaf handler, don't use *User in code which is called by some defer usage in the leaf handler, other than recycleUser.
ensuring the coding convention by technical means. My only idea here would be to place the *User inside some struct and only use that struct for one request and mark it as empty, once *User is put back into the pool. Make all access methods of that struct check for emptyness and panic/log/whatever, if an empty struct is accessed. Place a pointer to that struct in the context. But that is probably pointless, as you now allocate that struct for each request, instead of a User, which probably was, what you tried to avoid.
A minor remark
You probably meant your middleware function to read like:
func middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// get user from pool
user := getUser()
// user must be recycled
ctx := context.WithValue(r.Context(), "user", user)
// probably do something more
next(w, r.WithContext(ctx))
}
}

Related

How to ensure that a Struct that is made up of channels and map gets passed by reference?

I have the following struct that contains channels and a map for storage of data. I want to be able to pass that struct into functions in order to make use of those channels so that once they are triggered/have incoming messages, to use them in order to update the map that is associated with it.
I understand that maps by default are passed by reference when sent to various functions. Would this be the same case even when they are contained within a custom struct? How do i make sure that my entire struct is passed around to functions by reference in order to update Storage and also make use of its channels?
type CustomStrct struct {
Storage map[string]string
RetrieveChannel chan string
InsertChannel chan string
}
This is a constructor I have created for initialising a new instance of the struct:
func InitializeNewStore() CustomStrct {
newCustomStruct := CustomStrct {
Storage: make(map[string]string),
RetrieveChannel: make(chan Request),
InsertChannel: make(chan Request),
}
return newCustomStruct
}
Slices, maps and channels are pointer-like values in Go: copying a struct containing a channel copies a reference to the channel, not the channel itself:
a := CustomStrct{
RetrieveChannel: make(chan Request),
}
b := a
log.Println(a.RetrieveChannel == b.RetrieveChannel) // logs true
So it's quite fine to pass your struct either by value or by reference.
If you need to ensure that go vet will flag attempts to pass your struct by value, the simplest solution is to embed a sync.Mutex inside the struct:
type CustomStrct struct {
mu sync.Mutex
...
}
You don't need to actually use the mutex: just having it embedded in the struct will cause go vet to complain whenever you attempt to pass it by value.

Reliable thread-safe map

I was making a WaitForResponse function for my Discord bot, and it works, but the user can still use commands even when the bot is expecting a response. I combated this by using a map with the user and channel IDs, but I was then hit with the dreaded fatal error: concurrent map read and write. So I tried using a sync.Map, however it wouldn't always work when I spammed the command. I could sometimes still run commands when the bot was expecting a response. Is there any way I can ensure that the values are getting added and removed from the map when and as they should?
For these scenarios, sync.Mutex can be used to ensure that only one modification is allowed by acquiring a lock around the code that you want to be thread-safe.
var mu sync.Mutex
func readMap(key string) {
mu.Lock()
defer mu.Unlock()
return yourMap[key]
}
func updateMap(key, value string) {
mu.Lock()
defer mu.Unlock()
yourMap[key] = value
}
Mutex ensures that ONLY ONE goroutine can is allowed access to the locked code, which means for your case, only one operation, either read or write can be performed.
For better efficiency, you should consider using sync.RWMutex since you might not want to lock the map when it's being read. From GoDoc:
A RWMutex is a reader/writer mutual exclusion lock. The lock can be held by an arbitrary number of readers or a single writer. The zero value for a RWMutex is an unlocked mutex.
var mu sync.RWMutex
func readMap(key string) {
mu.RLock()
defer mu.RUnlock()
return yourMap[key]
}
func updateMap(key, value string) {
mu.Lock()
defer mu.Unlock()
yourMap[key] = value
}

Check errors when calling http.ResponseWriter.Write()

Say I have this http handler:
func SomeHandler(w http.ResponseWriter, r *http.Request) {
data := GetSomeData()
_, err := w.Write(data)
}
Should I check the error returned by w.Write? Examples I've seen just ignore it and do nothing. Also, functions like http.Error() do not return an error to be handled.
It's up to you. My advice is that unless the documentation of some method / function explicitly states that it never returns a non-nil error (such as bytes.Buffer.Write()), always check the error and the least you can do is log it, so if an error occurs, it will leave some mark which you can investigate should it become a problem later.
This is also true for writing to http.ResponseWriter.
You might think ResponseWriter.Write() may only return errors if sending the data fails (e.g. connection closed), but that is not true. The concrete type that implements http.ResponseWriter is the unexported http.response type, and if you check the unexported response.write() method, you'll see it might return a non-nil error for a bunch of other reasons.
Reasons why ResponseWriter.Write() may return a non-nil error:
If the connection was hijacked (see http.Hijacker): http.ErrHijacked
If content length was specified, and you attempt to write more than that: http.ErrContentLength
If the HTTP method and / or HTTP status does not allow a response body at all, and you attempt to write more than 0 bytes: http.ErrBodyNotAllowed
If writing data to the actual connection fails.
Even if you can't do anything with the error, logging it may be of great help debugging the error later on. E.g. you (or someone else in the handler chain) hijacked the connection, and you attempt to write to it later; you get an error (http.ErrHijacked), logging it will reveal the cause immediately.
Tip for "easy" logging errors
If you can't do anything with the occasional error and it's not a "showstopper", you may create and use a simple function that does the check and logging, something like this:
func logerr(n int, err error) {
if err != nil {
log.Printf("Write failed: %v", err)
}
}
Using it:
logerr(w.Write(data))
Tip for "auto-logging" errors
If you don't even want to use the logerr() function all the time, you may create a wrapper for http.ResponseWriter which does this "automatically":
type LogWriter struct {
http.ResponseWriter
}
func (w LogWriter) Write(p []byte) (n int, err error) {
n, err = w.ResponseWriter.Write(p)
if err != nil {
log.Printf("Write failed: %v", err)
}
return
}
Using it:
func SomeHandler(w http.ResponseWriter, r *http.Request) {
w = LogWriter{w}
w.Write([]byte("hi"))
}
Using LogWriter as a wrapper around http.ResponseWriter, should writes to the original http.ResponseWriter fail, it will be logged automatically.
This also has the great benefit of not expecting a logger function to be called, so you can pass a value of your LogWriter "down" the chain, and everyone who attempts to write to it will be monitored and logged, they don't have to worry or even know about this.
But care must be taken when passing LogWriter down the chain, as there's also a downside to this: a value of LogWriter will not implement other interfaces the original http.ResponseWriter might also do, e.g. http.Hijacker or http.Pusher.
Here's an example on the Go Playground that shows this in action, and also shows that LogWriter will not implement other interfaces; and also shows a way (using 2 "nested" type assertions) how to still get out what we want from LogWriter (an http.Pusher in the example).
I want to add to #icza solution. You don't need to create logging structure, you can use simple function:
func logWrite(write func([]byte) (int, error), body []byte) {
_, err := write(body)
if err != nil {
log.Printf("Write failed: %v", err)
}
}
Take a look on that approach based on the #icza code: https://play.golang.org/p/PAetVixCgv4

How to return data to sender of a channel

I am a Golang newbiew, and I am trying to implement an http server that synchronizes access to an ultra expensive computational (SAT) operation using a channel.
So I'd have these concurrent requests coming in, they'd pass their data to a channel, and a processing goroutine would pick up the data from the channel and perform the expensive operation, but after its done, what is the best way to return the result to the sender so the sender can send the http response?
See this answer as well.
Channels are first class types in Go,
you can just include a "response" channel in the request itself.
E.g. something like:
type Request struct {
Input int
RespC chan *Responce
}
type Response struct {
Result int
Err error
}
Service:
for req := range ReqC {
// start go routine or whatever
req.RespC <- &Result{Err: errors.New("not implemented")}
}
Requester:
c := make(chan *Response)
ReqC <- &Request{Input: 42, RespC: c}
res := <-c
// check res.Err, use res.Result
Where Request and Response can contain whatever fields you need.
If the structs are small (like this example) use chan Response instead of chan *Response (and the same for Request).

How to store or cache values to be served in http requests in Golang?

I am writing some Go web services (also implementing the webserver in Go with http.ListenAndServe).
I have a map of structs which I would like to keep in memory (with an approximate data size of 100Kb) to be used by different HTTP requests.
How can this be achieved in Go? I am thinking to use global package variables or caching systems (like memcache/groupcache).
In addition to the answers you've already received, consider making use of receiver-curried method values and http.HandlerFunc.
If your data is data that is loaded before the process starts, you could go with something like this:
type Common struct {
Data map[string]*Data
}
func NewCommon() (*Common, error) {
// load data
return c, err
}
func (c *Common) Root(w http.ResponseWriter, r *http.Request) {
// handler
}
func (c *Common) Page(w http.ResponseWriter, r *http.Request) {
// handler
}
func main() {
common, err := NewCommon()
if err != nil { ... }
http.HandleFunc("/", common.Root)
http.HandleFunc("/page", common.Page)
http.ListenAndServe(...)
}
This works nicely if all of the Common data is read-only. If the Common data is read/write, then you'll want to have something more like:
type Common struct {
lock sync.RWMutex
data map[string]Data // Data should probably not have any reference fields
}
func (c *Common) Get(key string) (*Data, bool) {
c.lock.RLock()
defer c.lock.RUnlock()
d, ok := c.data[key]
return &d, ok
}
func (c *Common) Set(key string, d *Data) {
c.lock.Lock()
defer c.lock.Unlock()
c.data[key] = *d
}
The rest is basically the same, except instead of accessing the data through the receiver's fields directly, you'd access them through the getters and setters. In a webserver where most of the data is being read, you will probably want an RWMutex, so that reads can be executed concurrently with one another. Another advantage of the second approach is that you've encapsulated the data, so you can add in transparent writes to and/or reads from a memcache or a groupcache or something of that nature in the future if your application grows such a need.
One thing that I really like about defining my handlers as methods on an object is that it makes it much easier to unit test them: you can easily define a table driven test that includes the values you want and the output you expect without having to muck around with global variables.
Don't indulge in premature optimization. Define a Go package API to encapsulate the data and then you can change the implementation at any time. For example, just scribbling,
package data
type Key struct {
// . . .
}
type Data struct {
// . . .
}
var dataMap map[Key]Data
func init() {
dataMap = make(map[Key]Data)
}
func GetData(key Key) (*Data, error) {
data := dataMap[key]
return &data, nil
}

Resources