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
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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.
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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
}
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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)
func deleteResourceUsingIdkey(idkey string) { // do delete here }
Any help.
Shulhan
unread,
Nov 17, 2020, 8:48:44 AM11/17/20
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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.
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).
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
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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.
>
> 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].
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
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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
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
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to golang-nuts
Hi,
THanks!!
BR
Abraham
b.ca...@pobox.com
unread,
Nov 27, 2020, 6:28:53 AM11/27/20
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
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)