How to detect HTTP client stop polling at the server side

297 views
Skip to first unread message

Afriyie Abraham Kwabena

unread,
Nov 14, 2020, 8:07:50 AM11/14/20
to golang-nuts
Hi,

My  question is  about multiple HTTP client polling an HTTP server randomly with a PATCH request to update a resource at the server running in front of MongoDB. The clients poll with their different ids and a valid time in their request. At the server, I can keep their ids and their different times. 
How can detect at the server if a specific client stop polling. I would like to write a function that detect at the HTTP server if a specific client stop polling and then remove its resources after some time from a database.  Am using gorilla/mux package for the server. Any  help, am new to Golang programming. 

Thanks in advance

Shulhan

unread,
Nov 15, 2020, 2:01:27 AM11/15/20
to Afriyie Abraham Kwabena, golang-nuts
I think this is not Go specific, but more like engineering in general.

My suggestion is you can store the last time when user request to PATCH endpoint in some storage or internal memory (like map[userID]timestamp) and then have some worker that check the timestamp to clean it up every X seconds or minutes.

Afriyie Abraham Kwabena

unread,
Nov 16, 2020, 4:25:32 AM11/16/20
to golang-nuts
Hi ,

You are right but am new to programming and currently this what i have done.
I have an http server handler that receives the PATCH request and stores the id and the current time stamp of the request.
But my problem is how to write the worker function to do the clean up every X seconds.

Code:
func UpdateData(response http.ResponseWriter, request *http.Request) {

    var (
        localVarHTTPMethod = http.MethodPatch
        patchItems         model.PatchItem
    )

    id := config.GetIdFromRequest(request)

    if request.Method == localVarHTTPMethod {

        err := json.NewDecoder(request.Body).Decode(&patchItems)
        if err != nil {
            common.WriteError(response, common.ErrBadRequest)
            return
        }

        defer request.Body.Close()

        var idtime = make(map[string]string)

        delete(idtime, id) // delete if already exist
        idtime[id] = time.Now() // store id and time stamp in map

        // how do i write a worker to the clean up after every X seconds?

    } else {
        common.WriteError(response, common.ErrMethodNotAllowed)
        return
    }
}

BR
Abraham

Shulhan

unread,
Nov 17, 2020, 1:12:18 AM11/17/20
to Afriyie Abraham Kwabena, golang-nuts


> On 16 Nov 2020, at 16.24, Afriyie Abraham Kwabena <afriyie...@gmail.com> wrote:
>
> Hi ,
>
> You are right but am new to programming and currently this what i have done.
> I have an http server handler that receives the PATCH request and stores the id and the current time stamp of the request.
> But my problem is how to write the worker function to do the clean up every X seconds.
>
> Code:
> func UpdateData(response http.ResponseWriter, request *http.Request) {
>
> var (
> localVarHTTPMethod = http.MethodPatch
> patchItems model.PatchItem
> )
>
> id := config.GetIdFromRequest(request)
>
> if request.Method == localVarHTTPMethod {
>
> err := json.NewDecoder(request.Body).Decode(&patchItems)
> if err != nil {
> common.WriteError(response, common.ErrBadRequest)
> return
> }
>
> defer request.Body.Close()
>
> var idtime = make(map[string]string)
>
> delete(idtime, id) // delete if already exist
> idtime[id] = time.Now() // store id and time stamp in map

You should store idtime inside the server/service type (the one that have HTTP handlers). For example,

----
type Server struct {
idtime map[string]string
}

func (my *Server) UpdateData(...) {
...
my.idtime[id] = time.Now()
...
}
----

>
> // how do i write a worker to the clean up after every X seconds?
>

Create a function that loop forever and loop the map to check the time, then run the function using goroutine before you start your HTTP server.

> } else {
> common.WriteError(response, common.ErrMethodNotAllowed)
> return
> }
> }
>



Anderson Queiroz

unread,
Nov 17, 2020, 3:34:39 AM11/17/20
to golang-nuts
Just one thing to keep in mind. Likely you have more than one serve instance running to process the requests. Thus it might happen the client will poll a different server on every request. Just imagine you have servers A, B, C behind a load balance and the domain example.com. As the client is pooling example.com, the first request might reach A, the second B and the third C. Now you have the 3 servers tracking the same client. It might happen server A doesn't receive any request from the client for a while, but not because the client isn't pooling any more, but because all requests are being directed to either B or C.

Afriyie Abraham Kwabena

unread,
Nov 17, 2020, 8:12:59 AM11/17/20
to golang-nuts
HI,

This is what I have tried so far but am not able to get the time difference in seconds. I mean the time differences between the time stored in the map[string]time.Time and
the current time in seconds.

code:

type Server struct {
    idtime map[string]time.Time
}

var my = Server{}

func main() {
    r := mux.NewRouter()
    usersData := r.PathPrefix("/users").Subrouter()
    usersData.Path("/{id}").Methods(http.MethodPatch).HandlerFunc(UpdateData)

    fmt.Println("Start listening")
    fmt.Println(http.ListenAndServe(":8080", r))

}

func UpdateData(response http.ResponseWriter, request *http.Request) {

    var (
        localVarHTTPMethod = http.MethodPatch
        patchItems         model.PatchItem
    )

    id := config.GetIdFromRequest(request)

    if request.Method == localVarHTTPMethod {

        err := json.NewDecoder(request.Body).Decode(&patchItems)
        if err != nil {
            common.WriteError(response, common.ErrBadRequest)
            return
        }

        defer request.Body.Close()

        my.idtime = make(map[string]time.Time)
        my.idtime[id] = time.Now()

        go func() {
            for keyid, t := range my.idtime {

                ts := t.Format(time.RFC3339)

                v, err := time.Parse(time.RFC3339, ts)
                if err != nil {
                    fmt.Println(err)
                    os.Exit(1)
                }

                timeRemaining := getTimeRemaining(v)

                if timeRemaining.S >= 60 {
                    // delete resouce in database after 60 seconds
                    deleteResourceUsingIdkey(keyid)
                }
            }
        }()

        common.RespondWith3gppJsonPatchJson(response, http.StatusNoContent, nil)
    } else {

        common.WriteError(response, common.ErrMethodNotAllowed)
        return
    }
}

type count struct {
    S int
}

func getTimeRemaining(t time.Time) count {
    currentTime := time.Now()
    difference := t.Sub(currentTime)

    seconds := int(difference.Seconds())

    return count{
        S: seconds,
    }
}

func deleteResourceUsingIdkey(idkey string) {
    // do delete here
}

Any help.

Shulhan

unread,
Nov 17, 2020, 8:48:44 AM11/17/20
to Afriyie Abraham Kwabena, golang-nuts
Hi Afriyie,

Looks like you almost there ;)

> On 17 Nov 2020, at 20.11, Afriyie Abraham Kwabena <afriyie...@gmail.com> wrote:
>
> HI,
>
> This is what I have tried so far but am not able to get the time difference in seconds. I mean the time differences between the time stored in the map[string]time.Time and
> the current time in seconds.
>
> code:
>
> type Server struct {
> idtime map[string]time.Time
> }
>
> var my = Server{}
>
> func main() {
> r := mux.NewRouter()
> usersData := r.PathPrefix("/users").Subrouter()
> usersData.Path("/{id}").Methods(http.MethodPatch).HandlerFunc(UpdateData)
>

Based on my understanding (I have never use mux before), the UpdateDate function will be running concurrently as goroutine. Lets say that we have three PATCH requests at the same time, there will be three UpdateData running concurrently or in parallel.

> fmt.Println("Start listening")
> fmt.Println(http.ListenAndServe(":8080", r))
> }
>
> func UpdateData(response http.ResponseWriter, request *http.Request) {
>
> var (
> localVarHTTPMethod = http.MethodPatch
> patchItems model.PatchItem
> )
>
> id := config.GetIdFromRequest(request)
>
> if request.Method == localVarHTTPMethod {
>
> err := json.NewDecoder(request.Body).Decode(&patchItems)
> if err != nil {
> common.WriteError(response, common.ErrBadRequest)
> return
> }
>
> defer request.Body.Close()
>
> my.idtime = make(map[string]time.Time)

Since the UpdateData is running independently for each request, you should initialize this once, in the main, otherwise each UpdateData routine will reset the idtime variable.

> my.idtime[id] = time.Now()
>
> go func() {
> for keyid, t := range my.idtime {
>
> ts := t.Format(time.RFC3339)
>
> v, err := time.Parse(time.RFC3339, ts)
> if err != nil {
> fmt.Println(err)
> os.Exit(1)
> }
>
> timeRemaining := getTimeRemaining(v)
>
> if timeRemaining.S >= 60 {
> // delete resouce in database after 60 seconds
> deleteResourceUsingIdkey(keyid)
> }
> }
> }()

Also, you should move the above goroutine to main, otherwise each call to UpdateData will spawn a new goroutine.

If I were you I will use the Unix time [1] instead of Time object, its much simpler `time.Now().Unix() - my.idtime[keyid] >= 60`.

BTW, beware of race condition. Test or build your program with "-race" option to see it by yourself. Use sync.Mutex [2] to prevent the write (updating the map) overlap with read (checking the elapsed seconds).

[1] https://pkg.go.dev/time#Time.Unix
[2] https://pkg.go.dev/sync#Mutex

>
> common.RespondWith3gppJsonPatchJson(response, http.StatusNoContent, nil)
> } else {
>
> common.WriteError(response, common.ErrMethodNotAllowed)
> return
> }

Another things that I can't not comment, you can simplify the code by returning-first, which minimize code identation.

Afriyie Abraham Kwabena

unread,
Nov 17, 2020, 12:33:50 PM11/17/20
to golang-nuts
Hi,

I have made changes according to the comments and also simply the main function but am not sure if the worker go routine works.
This what i did:

type Route struct {
    Name        string
    Method      string
    Pattern     string
    HandlerFunc http.HandlerFunc
}

var idtime = make(map[string]int64)


func UpdateData(response http.ResponseWriter, request *http.Request) {

    var (
        localVarHTTPMethod = http.MethodPatch
        patchItems         model.PatchItem
    )

    id := config.GetIdFromRequest(request)

    if request.Method == localVarHTTPMethod {

        err := json.NewDecoder(request.Body).Decode(&patchItems)
        if err != nil {
            common.WriteError(response, common.ErrBadRequest)
            return
        }

        defer request.Body.Close()

        idtime[id] = time.Now().Unix()


        common.RespondWith3gppJsonPatchJson(response, http.StatusNoContent, nil)
    } else {
        common.WriteError(response, common.ErrMethodNotAllowed)
        return
    }
}

var (
    mu  sync.Mutex
    ctx = context.Background()
)

func worker() {
    mu.Lock()
    for keyid := range idtime {

        d := time.Now().Unix() - idtime[keyid]
        if d >= 60 {

            // delete resouce in database after 60 seconds
            _ = DeleteNFInstance(ctx, keyid)
        }
    }
    mu.Unlock()
}

// Delete info
func DeleteNFInstance(ctx context.Context, nfinstanceId string) bool {
    filter := bson.M{"_id": nfinstanceId}
    _, err := db.Collection(COLLECTION).DeleteOne(ctx, filter)
    if err != nil {
        return false
    }
    return true
}

type Routes []Route

func NewRouter() *mux.Router {
    router := mux.NewRouter().StrictSlash(true)
    for _, route := range routes {
        var handler http.Handler
        handler = route.HandlerFunc

        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(handler)
    }

    return router
}

var routes = Routes{

    Route{
        "UpdateData",
        strings.ToUpper("Patch"),
        "/users/{id}",
        UpdateData,
    },
}

// main function
func main() {
    r := NewRouter()

    go worker()


    fmt.Println("Start listening")
    fmt.Println(http.ListenAndServe(":8080", r))
}

I appreciate your help but am still not able to it work.

Afriyie Abraham Kwabena

unread,
Nov 17, 2020, 1:07:04 PM11/17/20
to golang-nuts
Hi,

The UpdateData function is the HTTP handler for the route which matches the URL and  is called after the mux.Router after receiving an incoming request matches the incoming request
 against the registered route.

BR
Afriyie

Shulhan

unread,
Nov 17, 2020, 2:41:01 PM11/17/20
to Afriyie Abraham Kwabena, golang-nuts


> On 18 Nov 2020, at 01.06, Afriyie Abraham Kwabena <afriyie...@gmail.com> wrote:
>
> Hi,
>
> The UpdateData function is the HTTP handler for the route which matches the URL and is called after the mux.Router after receiving an incoming request matches the incoming request
> against the registered route.

...

> var idtime = make(map[string]int64)
>
>
> func UpdateData(response http.ResponseWriter, request *http.Request) {
>
> var (
> localVarHTTPMethod = http.MethodPatch
> patchItems model.PatchItem
> )
>
> id := config.GetIdFromRequest(request)
>
> if request.Method == localVarHTTPMethod {
>
> err := json.NewDecoder(request.Body).Decode(&patchItems)
> if err != nil {
> common.WriteError(response, common.ErrBadRequest)
> return
> }
>
> defer request.Body.Close()
>
> idtime[id] = time.Now().Unix()
>

We still may have data race here.

>
> func worker() {
> mu.Lock()
> for keyid := range idtime {
>
> d := time.Now().Unix() - idtime[keyid]
> if d >= 60 {
>
> // delete resouce in database after 60 seconds
> _ = DeleteNFInstance(ctx, keyid)
> }
> }
> mu.Unlock()
> }
>

...

> // main function
> func main() {
> r := NewRouter()
>
> go worker()
>
>
> fmt.Println("Start listening")
> fmt.Println(http.ListenAndServe(":8080", r))
> }
>
> I appreciate your help but am still not able to it work.
>

Looks like your worker only loop once and then it finished. Either you use time.Sleep() to repeat the loop inside loop or use time.Ticker [1].

[1] https://pkg.go.dev/time#Ticker

Afriyie Abraham Kwabena

unread,
Nov 18, 2020, 3:51:49 AM11/18/20
to golang-nuts
Hi Sulhan,  Anderson,

Thanks for your guidance. It works now using time.Ticker

func worker() {
    tiker := time.NewTicker(30 * time.Second)
    for _ = range tiker.C {

        mu.Lock()
        for keyid := range idtime {

            d := time.Now().Unix() - idtime[keyid]
            if d >= 60 {
                // delete resouce in database after 60 seconds
                _ = DeleteNFInstance(ctx, keyid)
            }
        }
        mu.Unlock()
    }
}

BR
Abraham

Afriyie Abraham Kwabena

unread,
Nov 26, 2020, 7:07:43 PM11/26/20
to golang-nuts
Hi,

Am experiancing data race warning with my pollingworker function below even though when i build with the  -race flag i do not get any error. Any help?

func PollingWorker() {
    tiker := time.NewTicker(60 * time.Second)

    for _ = range tiker.C {
        mu.Lock()
        for keyid := range idtime {
            d := time.Now().Unix() - idtime[keyid]
            if d >= 60 {
                // Find nfinstance using keyid
                nfinstanceProfile, err := config.Conf.FindNFInstanceProfileById(ctx, keyid)
                if err != nil {
                    config.Logrus.Error("Could not find NF Instance Profile with ID :" + keyid + " to SUSPEND Status")
                }
                // change nfStatus of nfinstance to SUSPENDED
                err = config.Conf.SuspendNFInstanceNfStatus(ctx, nfinstanceProfile, keyid)
                if err != nil {
                    config.Logrus.Error(err)
                }
                delete(idtime, keyid)
            }
        }
        mu.Unlock()
    }
}


BR
Abraham

Shulhan

unread,
Nov 26, 2020, 11:38:52 PM11/26/20
to Afriyie Abraham Kwabena, golang-nuts


> On 27 Nov 2020, at 07.06, Afriyie Abraham Kwabena <afriyie...@gmail.com> wrote:
>
> Hi,
>
> Am experiencing data race warning with my pollingworker function below even though when i build with the -race flag i do not get any error. Any help?
>

Usually when you got data race warning it will print the line that cause read and write race condition. Pay attention to both lines and I think you will find the cause.

It's kind of hard to figure it out only through a single function. It could be in config.Conf or in function that manipulate idtime outside the pollingworker.

Afriyie Abraham Kwabena

unread,
Nov 27, 2020, 1:14:48 AM11/27/20
to golang-nuts
Hi,

Yes from the warning it prints the handler function below and after google searching what i did was to mutex.Lock and Unlock the handler function and no warning again.
However, my google search people mention of using channels instead of mutex. What I would like to ask is, using mutex OK and if not the best way of solving it, how can i use
channels in this case.

Handler function below:

func UpdateNFInstance(response http.ResponseWriter, request *http.Request) {

    mu.Lock()
    defer mu.Unlock()


    var (
        localVarHTTPMethod = http.MethodPatch
        ctx                = context.Background()

        patchItems         model.PatchItem
    )

    id := config.GetIdFromRequest(request)

    if request.Method == localVarHTTPMethod {

        if request.Header.Get("Content-Type") != "application/json-patch+json" {
            common.WriteError(response, common.ErrStatusUnsupportedMediaTypePatch)
            return

        }

        err := json.NewDecoder(request.Body).Decode(&patchItems)
        if err != nil {
            common.WriteError(response, common.ErrBadRequest)
            return
        }

        defer request.Body.Close()

        delete(idtime, id) // delete value in map if exit

        idtime[id] = time.Now().Unix()

        nfProfile, err := config.Conf.FindNFInstanceProfileById(ctx, id)
        if err != nil {
            common.WriteError(response, common.ErrNotFound)
            return
        }

        if patchItems.Path == "/nfStatus" {

            nfProfile.NfStatus = patchItems.Value
            nfProfile.LoadTimeStamp = time.Now().String()

            success := config.Conf.UpdateNFInstanceHeartBeatNfStatus(ctx, nfProfile, id)
            if !success {
                common.WriteError(response, common.ErrInternalServer)
                return
            }

            if request.URL.Scheme != "" {
                scheme = request.URL.Scheme
            }
            response.Header().Set("Content-Location", scheme+"://"+request.Host+request.URL.Path)
            response.Header().Set("Response-Desc", "Success")
            common.RespondWith3gppJSONPatchJSON(response, http.StatusNoContent, nil)

        } else {
            // patchItems.Path == "/load" information
            v, err := strconv.Atoi(patchItems.Value)
            if err != nil {
                config.Logrus.Errorf("Failed to convert Patch Item Value string to integer: %s", err.Error())
            }

            nfProfile.Load = int32(v)
            nfProfile.LoadTimeStamp = time.Now().String()

            success := config.Conf.UpdateNFInstanceHeartBeatLoad(ctx, nfProfile, id)
            if !success {
                common.WriteError(response, common.ErrInternalServer)
                return
            }

            if request.URL.Scheme != "" {
                scheme = request.URL.Scheme
            }
            response.Header().Set("Content-Location", scheme+"://"+request.Host+request.URL.Path)
            response.Header().Set("Response-Desc", "Success")
            common.RespondWith3gppJSONPatchJSON(response, http.StatusNoContent, nil)

        }
    } else {
        common.WriteError(response, common.ErrMethodNotAllowed)
        return
    }
}


BR
Abraham

Afriyie Abraham Kwabena

unread,
Nov 27, 2020, 1:25:29 AM11/27/20
to golang-nuts
Hi,

I just produce the warning for clarity. sorry for the huge message


==================
WARNING: DATA RACE
Write at 0x00c0002d6480 by goroutine 48:
  runtime.mapdelete_faststr()
      /usr/local/go/src/runtime/map_faststr.go:297 +0x0
  nfnrfapi/services/manag.UpdateNFInstance()
      /home/xxx/go/src/nfnrfapi/services/manag/api_nf_instance_id_document_Update.go:48 +0x46d
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  nfnrfapi/common.RecoverHandler.func1()
      /home/xxx/go/src/nfnrfapi/common/middlewares.go:56 +0xe6
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  github.com/NYTimes/gziphandler.GzipHandlerWithOpts.func1.1()
      /home/xxx/go/pkg/mod/github.com/!n!y!times/gziph...@v1.1.1/gzip.go:336 +0x45e
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  github.com/gorilla/mux.(*Router).ServeHTTP()
      /home/xxx/go/pkg/mod/github.com/gorilla/m...@v1.8.0/mux.go:210 +0x132
  nfnrfapi/common.Logging.func1.1()
      /home/xxx/go/src/nfnrfapi/common/middlewares.go:42 +0xcf
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  nfnrfapi/common.Tracing.func1.1()
      /home/xxx/go/src/nfnrfapi/common/middlewares.go:26 +0x454
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  net/http.serverHandler.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2843 +0xca
  net/http.initALPNRequest.ServeHTTP()
      /usr/local/go/src/net/http/server.go:3415 +0x104
  net/http.(*initALPNRequest).ServeHTTP()
      <autogenerated>:1 +0xa6
  net/http.Handler.ServeHTTP-fm()
      /usr/local/go/src/net/http/server.go:87 +0x64
  golang.org/x/net/http2.(*serverConn).runHandler()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:2152 +0xc5

Previous read at 0x00c0002d6480 by goroutine 18:
  runtime.mapiterinit()
      /usr/local/go/src/runtime/map.go:797 +0x0
  nfnrfapi/services/manag.PollingWorker()
      /home/xxx/go/src/nfnrfapi/services/manag/api_nf_instance_id_document_Update.go:111 +0x604

Goroutine 48 (running) created at:
  golang.org/x/net/http2.(*serverConn).processHeaders()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:1882 +0x924
  golang.org/x/net/http2.(*serverConn).processFrame()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:1410 +0x41e
  golang.org/x/net/http2.(*serverConn).processFrameFromReader()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:1368 +0x7d9
  golang.org/x/net/http2.(*serverConn).serve()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:869 +0x14fc
  golang.org/x/net/http2.(*Server).ServeConn()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:472 +0xdc4
  golang.org/x/net/http2.ConfigureServer.func1()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:298 +0x11e
  net/http.(*conn).serve()
      /usr/local/go/src/net/http/server.go:1834 +0x1d5b

Goroutine 18 (running) created at:
  main.main()
      /home/xxx/go/src/nfnrfapi/main.go:41 +0x2c6
==================
==================
WARNING: DATA RACE
Write at 0x00c0000f7a88 by goroutine 48:
  nfnrfapi/services/manag.UpdateNFInstance()
      /home/xxx/go/src/nfnrfapi/services/manag/api_nf_instance_id_document_Update.go:50 +0x53c
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  nfnrfapi/common.RecoverHandler.func1()
      /home/xxx/go/src/nfnrfapi/common/middlewares.go:56 +0xe6
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  github.com/NYTimes/gziphandler.GzipHandlerWithOpts.func1.1()
      /home/xxx/go/pkg/mod/github.com/!n!y!times/gziph...@v1.1.1/gzip.go:336 +0x45e
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  github.com/gorilla/mux.(*Router).ServeHTTP()
      /home/xxx/go/pkg/mod/github.com/gorilla/m...@v1.8.0/mux.go:210 +0x132
  nfnrfapi/common.Logging.func1.1()
      /home/xxx/go/src/nfnrfapi/common/middlewares.go:42 +0xcf
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  nfnrfapi/common.Tracing.func1.1()
      /home/xxx/go/src/nfnrfapi/common/middlewares.go:26 +0x454
  net/http.HandlerFunc.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2042 +0x51
  net/http.serverHandler.ServeHTTP()
      /usr/local/go/src/net/http/server.go:2843 +0xca
  net/http.initALPNRequest.ServeHTTP()
      /usr/local/go/src/net/http/server.go:3415 +0x104
  net/http.(*initALPNRequest).ServeHTTP()
      <autogenerated>:1 +0xa6
  net/http.Handler.ServeHTTP-fm()
      /usr/local/go/src/net/http/server.go:87 +0x64
  golang.org/x/net/http2.(*serverConn).runHandler()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:2152 +0xc5

Previous read at 0x00c0000f7a88 by goroutine 18:
  nfnrfapi/services/manag.PollingWorker()
      /home/xxx/go/src/nfnrfapi/services/manag/api_nf_instance_id_document_Update.go:112 +0x1c4

Goroutine 48 (running) created at:
  golang.org/x/net/http2.(*serverConn).processHeaders()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:1882 +0x924
  golang.org/x/net/http2.(*serverConn).processFrame()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:1410 +0x41e
  golang.org/x/net/http2.(*serverConn).processFrameFromReader()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:1368 +0x7d9
  golang.org/x/net/http2.(*serverConn).serve()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:869 +0x14fc
  golang.org/x/net/http2.(*Server).ServeConn()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:472 +0xdc4
  golang.org/x/net/http2.ConfigureServer.func1()
      /home/xxx/go/pkg/mod/golang.org/x/n...@v0.0.0-20201031054903-ff519b6c9102/http2/server.go:298 +0x11e
  net/http.(*conn).serve()
      /usr/local/go/src/net/http/server.go:1834 +0x1d5b

Goroutine 18 (running) created at:
  main.main()
      /home/xxx/go/src/nfnrfapi/main.go:41 +0x2c6

b.ca...@pobox.com

unread,
Nov 27, 2020, 4:12:53 AM11/27/20
to golang-nuts
On Friday, 27 November 2020 at 06:14:48 UTC Afriyie Abraham Kwabena wrote:
What I would like to ask is, using mutex OK and if not the best way of solving it, how can i use
channels in this case.

There's nothing wrong with mutex, but you can use channels for a more native-Go experience.
This video is well worth watching: https://www.youtube.com/watch?v=5zXAHh5tJqQ

In short, you can get mutex or semaphore-like behaviour by having a channel with fixed depth, and putting/pulling values from it.

mutex := make(chan struct{}, 1)
...

mutex <- struct{}{}
... do stuff
<-mutex

Afriyie Abraham Kwabena

unread,
Nov 27, 2020, 4:29:43 AM11/27/20
to golang-nuts
Hi,

THanks!!

BR
Abraham

b.ca...@pobox.com

unread,
Nov 27, 2020, 6:28:53 AM11/27/20
to golang-nuts
I should add: using channels often means the mutex is not required.

Imagine, for example, that you want a concurrency-safe counter (that multiple goroutines can read, increment and decrement).  You can put the counter value into a buffered channel:

counter := make(chan int, 1)
counter <- 0

Then you pull the value out of channel while you're working on it, and put it back when you're finished.

// Read counter
cv := <- counter
fmt.Println("Counter is %d", cv)
counter <- cv

// Increment counter
counter <- (<-counter + 1)

This is all safe because only one goroutine has possession of the counter value at any time.  Just make sure you always put it back (it can be helpful to write functions to do the accessing, and use 'defer' to put the value back)
Reply all
Reply to author
Forward
0 new messages