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.

Reply via email to