Hi, This is what i have done so far but still having problems. When server A starts and server B is not available, it retry but if server B start running, sever A sends the PUT message in the SendNFInstanceRegistration(). When i print the status code in SendNFInstanceRegistration() i get
status:201 INFO[0014] Server A Profile Registration To SUCCESS status:200 INFO[0014] Server A Profile Update SUCCESS After this , the PATCH message is send OK but when server B stop running, server A break, complain g about the status check in the PATCH for loop status204ERRO[0350] Patch "http://127.0.0.1:9090/nnrf-nfm/v1/nf-instances/6ba7b810-9dad-11d1-80b4-00c04fd430c9": dial tcp 127.0.0.1:9090: connect: connection refused panic: runtime error: invalid memory address or nil pointer dereference [signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0xe6aa92] goroutine 47 [running]: PutAndPatch() register.go:147 +0x1d2 exit status 2 Am expecting the "break" in the PATCH message to restart the PUT message again until server B is up again Here is what i have done so far // Send PUT message func SendNFInstanceRegistration() (int32, string, *http.Response) { var profile Profile profilebytes, err := ioutil.ReadFile("./profile.json") if err != nil { logrus.Println("Cannot read json file") } // unmarshall it err = json.Unmarshal(profilebytes, &profile) if err != nil { logrus.Print(err) } pcfprofile.NfInstanceId = contx.ID id := contx.ID locProfilebytes, err := json.Marshal(profile) if err != nil { logrus.Println(err) } locVarUrl := NewConfig.BasePath + "/nf-instances/" + id htbt, contentLoc, resp, err := HandleNFInstanceRegistration(locProfilebytes, locVarUrl) if err != nil { logrus.Error("Server A could not register profile") } status := resp.StatusCode fmt.Print("status:", status) if status == http.StatusOK { logrus.Println("Server A Profile Update SUCCESS") } else if status == http.StatusCreated { logrus.Println("Server A Profile Registration To SUCCESS") } else { logrus.Println(fmt.Errorf("Wrong status code returned by Server B %d", status)) } heartBeatTimer := htbt return heartBeatTimer, contentLoc, resp } func HandleNFInstanceRegistration(nfprofilebyte []byte, VarPath string) (int32, string, *http.Response, error) { var ( // Set client and set url localVarHTTPMethod = http.MethodPut nfprofile Profile heartBeatTimer int32 contentloc string ) req, err := http.NewRequest(localVarHTTPMethod, VarPath, bytes.NewBuffer(nfprofilebyte)) if err != nil { logrus.Error(err) } req.Close = true req.Header.Set("Content-Type", "application/json") req.Header.Set("Accept", "application/json") backoff := 1 for { res, err := transport.Client.Do(req) //Use for dev if err != nil || res == nil { logrus.Println("Server A trying to register profile ...") backoff *= 2 if backoff > 20 { backoff = 20 } time.Sleep(time.Duration(backoff) * time.Second) continue } defer func() { if resCloseErr := res.Body.Close(); resCloseErr != nil { logrus.Errorf("response body cannot close: %+v", resCloseErr) } }() bodybytes, err := ioutil.ReadAll(res.Body) //localVarHTTPResponse.Body.Close() if err != nil { logrus.Error(err) return heartBeatTimer, contentloc, res, err } json.Unmarshal(bodybytes, &nfprofile) heartBeatTimer = nfprofile.HeartBeatTimer contentloc := res.Header.Get("Location") return heartBeatTimer, contentloc, res, nil } } // PATCH message // func SendHeartbeat(ContentLocation string) (response *http.Response, err error) { patchitem := []models.PatchItem{ models.PatchItem{ Op: "replace", Path: "/load", From: ContentLocation, Value: "REG, } } patchitembytes, err := json.Marshal(patchitem) if err != nil { logrus.Error(err) } req, err := http.NewRequest("PATCH", ContentLocation, bytes.NewBuffer(patchitembytes)) req.Header.Set("Content-Type", "application/json-patch+json") response, err = transport.Client.Do(req) // for dev if err != nil { logrus.Debug("Server A Heart-Beat Request FAILED") return response, err } defer response.Body.Close() status := response.StatusCode if status == http.StatusNoContent { logrus.Info("Heart-Beat Message SUCCESS") } else if status == http.StatusNotFound { logrus.Println("Heart-Beat Message FAILED") } return response, err } func PutAndPatch() { var interval int32 var resp *http.Response var cl string for { interval, cl, resp = SendNFInstanceRegistration() status := resp.StatusCode if status == http.StatusCreated || status == http.StatusOK { break } } ticker := time.NewTicker(time.Duration(interval) * time.Second) for _ = range ticker.C { res, err := SendHeartbeat(cl) if err != nil { logrus.Error(err) } fmt.Print("status", res.StatusCode) if res.StatusCode != http.StatusNoContent { break } } } func main(){ PutAndPatch() } On Saturday, March 20, 2021 at 9:19:54 PM UTC+2 Van Fury wrote: > Hi Brian, > > Thanks for the reply. I intentionally left the error checks out so as to > make the code short. > Also using the Client.Do was an error and its suppose to be > transport.Client.Do. > Also in the main() does other stuff after the PUT and PATCH messages but > does not have any function that is > using the data concurrently. > > Thanks for the advice on the code structure. I will restructure my code to > the PuTAndPatch(). > > BR > Van > > > > > > > On Saturday, March 20, 2021 at 12:04:33 PM UTC+2 Brian Candler wrote: > >> *> Any help and comments about my application design* >> >> A few general observations first: >> >> 1. In several places you are ignoring the error status, in particular at >> json.Unmarshal(bodybytes, &nfp) and the error from transport.Client.Do >> 2. In HandlePUTMessage you are ignoring the HTTP status code >> 3. I can't see why you're using transport.Client.Do in one function, and >> Client.Do in another >> 4. The "Location" response header, that you read in >> SendPUTMessageToServerB, should only be set on 3xx (redirect) responses, >> but you only accept 200 / 204 >> 5. Your main function starts a goroutine ("go >> SendPATCHMessageEveryTimerValue()"), but then immediately exits: >> https://play.golang.org/p/dLXvxfCaki4 . If there's no other stuff going >> on, then just remove the "go". (But maybe this is because this is an >> incomplete program fragment, and your real main() does other stuff) >> 6. You have clearly realised that you can't have two different goroutines >> reading and writing the same variable concurrently. However the way you've >> used mutex seems wrong here. Firstly, you're keeping the mutex locked >> indefinitely (you should only lock it for a short time, while performing an >> action that touches those variables), and secondly, I don't see any code in >> the main goroutine which would be touching the same data concurrently - >> which would also need to be protected by the mutex. But again, this could >> also be because the program is incomplete. >> >> *> restarting from step 1 when 404 status code is received does not work* >> >> I believe the problem is that you're not returning an "error" status from >> PATCHMessage under that condition. You can synthesise one of your own: >> >> } else if status == http.StatusNotFound { >> >> // Here I would like to restart PUT message if status code is 404 >> *return fmt.Errorf("Got http.StatusNotFound")* >> } >> >> ... which would then be caught by "if err != nil" in >> SendPATCHMessageEveryTimerValue. >> >> If you want, you could make your own object (compatible with the "error" >> interface) to carry the HTTP status as a value: >> >> type HttpError struct { >> Code int >> Description string >> } >> >> func (e HttpError) Error() string { >> return e.Description >> } >> >> ... >> if status != http.StatusOK { >> return &HttpError{ >> Code: resp.StatusCode, >> Description: fmt.Sprintf("Unexpected status: %s", >> resp.Status), >> } >> } >> >> This can be useful if the caller wants to see the HTTP status value and >> act on it: >> >> err := Foo() >> if e, ok := err.(*HttpError); ok && e.Code == >> http.StatusForbidden { >> .... do something >> } >> >> However, personally, I think you should structure the code more closely >> along the lines of your original description of the problem. >> >> What I mean is: instead of having SendPATCHMessageEveryTimerValue >> internally call out to SendPUTMessage if there's an error, I'd make it >> terminate. The caller is then responsible for going back to step 1 and >> sending the PUT before sending more PATCHes. >> >> This gives the overall structure something like this: >> >> func PutAndPatch() { >> for { >> var interval time.Duration >> for { >> ... do your step 1, 2, 3 >> ... break out when you have the valid PUT response and have >> set 'interval' >> } >> for { >> ... do your step 4, 5, 6 >> ... break out when there is a failure to PATCH >> } >> } >> } >> >> The caller can then choose to run this as a go routine, with "go >> PutAndPatch()", or not, as they prefer. Note also that the state ("var >> interval") is private to this function, so there's no need for a mutex. >> This would also allow you to run multiple instances of PutAndPatch, talking >> to different servers, in different goroutines. >> >> Of course, as it stands, if you run PutAndPatch() in a goroutine then it >> will run autonomously, and the main program will have no idea of its status >> - and not even have a way to stop it. If you want to add that sort of >> functionality, then the cleanest way is likely to be using channels. For >> example, the standard way to signal a "stop" is to have an empty channel, >> which the caller closes when they want the stop to take place; or to use >> the "context <https://blog.golang.org/context>" library which is a >> wrapper around this idea. For any sort of concurrency it's also well worth >> getting your head around the contents of this video: GopherCon 2018: >> Bryan C. Mills - Rethinking Classical Concurrency Patterns >> <https://www.youtube.com/watch?v=5zXAHh5tJqQ>. >> >> If you do decide to read and/or modify global state from PutAndPatch(), >> then you'd want to lock the mutex just before accessing it and unlock it >> immediately afterwards - and the main program would need to do the same. >> It's very easy to introduce bugs if you forget to do this. Use the race >> detector <https://blog.golang.org/race-detector> to help find such >> problems. >> >> HTH, >> >> Brian. >> > -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/golang-nuts/7aed948f-8935-4331-838e-c6359dc15693n%40googlegroups.com.