2

I'm new to Go and I have the following problem. I tried to simplify it: I have a server which has for example a global variable myvar. All users can POST the endpoint /step1 and save some data in the variable, which can be retrieved with a GET using the second endpoint /step2. Between these 2 calls the value of myvar shouldn't change for that user.

I would like to know if there is a way to instantiate this process for every user, because I need that if one user changes the variable, it doesn't affect the other users. I don't necessarily need to use the global variable, it is just to expose what I want to do with the endpoints.

Code:

package main

import (
    "encoding/json"
    "net/http"

    "github.com/gorilla/mux"
    "github.com/rs/cors"

    "fmt"
)

type Test struct {
    test string `json:"test,omitempty"`
}

func main() {
    var myvar = "test"

    router := mux.NewRouter()

    router.HandleFunc("/step1", func(w http.ResponseWriter, r *http.Request) {
        var test Test
        _ = json.NewDecoder(r.Body).Decode(&test)
        myvar = test.test
    })

    router.HandleFunc("/step2", func(w http.ResponseWriter, r *http.Request) {
        fmt.Println(myvar)
    })

    c := cors.New(cors.Options{
        AllowedOrigins:   []string{"*"},
        AllowCredentials: true,
        AllowedMethods:   []string{"GET", "POST", "PUT", "DELETE", "PATCH"},
        AllowedHeaders:   []string{"*"},
        ExposedHeaders:   []string{"*"},
    })

    handler := c.Handler(router)

    http.ListenAndServe(":8003", handler)
}
icza
  • 389,944
  • 63
  • 907
  • 827
Battalgazi
  • 367
  • 1
  • 4
  • 20

1 Answers1

4

Requests are served from multiple goroutines, concurrently. This means if they read/write the same variable, access to this variable must be synchronized.

Next, if you want a different instance of this data for each user, you may use a map, mapping from user ID or name to the data structure.

Let's assume the data structure is a struct, e.g.:

type customData struct {
    Field1 string
    Field2 int
    // Whatever fields you need
}

The map holding one for each user:

var userDataMap = map[string]customData{}

You may use a sync.RWMutex for protecting a map while it is read / written from a goroutine:

var mu = &sync.RWMutex{}

And synchronized access to the map, using the above mutex:

func Get(user string) customData {
    mu.RLock()
    defer mu.RUnlock()
    return userDataMap[user]
}

func Set(user string, data customData) {
    mu.Lock()
    userDataMap[user] = data
    mu.Unlock()
}

Another, more sophisticated solution would be to use server side HTTP sessions. For details, see Go session variables?

icza
  • 389,944
  • 63
  • 907
  • 827
  • Thanks for your answer, ill work on it and come back with feedback – Battalgazi May 19 '17 at 13:04
  • @Battalgazi I edited the code and changed `customData` pointers to be non-pointers (this might prevent further data races if you'd try to modify and set back the same `*customData` value). – icza May 19 '17 at 13:19
  • Thanks i could make it work using the map as u proposed :) – Battalgazi May 25 '17 at 12:13