*> 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/0208f9d2-62df-4bfd-b1d5-b61b5af53e80n%40googlegroups.com.

Reply via email to