ibm-messaging / mq-golang

Calling IBM MQ from Go applications
Apache License 2.0
168 stars 60 forks source link

MQCNO_RECONNECT and MQCBT_EVENT_HANDLER #128

Closed ravellan closed 4 years ago

ravellan commented 5 years ago

MQ v 9.1.2 go version go1.13.3 windows/amd64

I am trying to setup handling of qmgr ending or failing over and played around with setting up an MQCBT_EVENT_HANDLER to catch those events. When using MQCNO_RECONNECT and testing with endmqm -r it got a bit weird. To reproduce I make these small adjustments to the amqscb.go sample code.

cno.Options = ibmmq.MQCNO_CLIENT_BINDING + ibmmq.MQCNO_RECONNECT
//and what else is necessary for Connx client binding.

cbd.CallbackType = ibmmq.MQCBT_EVENT_HANDLER

//callback function changed to this
func cb(hConn *ibmmq.MQQueueManager //.... same arguments as original
    if err.MQCC != ibmmq.MQCC_OK {
        fmt.Println(err)
        ok = false
    }
}

Also adding to mqclient.ini for quick abortion of reconnection Channels: MQReconnectTimeout=15 Running this and ending qmgr with -r option results in this output.

Sample AMQSCB.GO start
Connected to queue manager QM1
Opened queue DEV.QUEUE.1
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_FAILED [2] MQRC = MQRC_RECONNECT_FAILED [2548]
MQCALLBACK: MQCC = MQCC_FAILED [2] MQRC = MQRC_CONNECTION_BROKEN [2009]
MQCLOSE: MQCC = MQCC_FAILED [2] MQRC = MQRC_HCONN_ASYNC_ACTIVE [2500]
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_WARNING [1] MQRC = MQRC_RECONNECTING [2544]
MQCALLBACK: MQCC = MQCC_FAILED [2] MQRC = MQRC_RECONNECT_FAILED [2548]
MQDISC: MQCC = MQCC_FAILED [2] MQRC = MQRC_RECONNECT_FAILED [2548]

This seems a bit weird since the MQ lib starts the reconnect procedure once more for the MQDISC call. Regardless since all CTL/Close/Disc call fails the cb function won't get cleared from the cbMap which doesn't feel right for a long running program that might reconnect later.

What is a proper programmer response to a MQRC_RECONNECT_FAILED [2548]?
Do we need another way of clearing the cb function(s) since all MQI calls fail?

ibmmqmet commented 5 years ago

I've been playing with this, and this seems to be essentially the same behaviour exhibited by the base C API (I used the amqsghac.c sample). There's not really anything more that the application programmer can do. The underlying callback registered with the C client does get seem to deleted by the MQDISC without any need for something like an explicit MQOP_DEREGISTER even though the MQDISC eventually gives a failure code.

While it's true that the Go wrapper will currently leave the callback info in the map, the callback won't be called again. If a subsequent new connection is successful with the process, and returns the same hConn value - which is likely - then any further requests for callbacks on that connection will overwrite the info in the map.

What I will do is change the Disc() function to always delete callback info from the map, even if there is an error. So that will make things a bit tidier, though it's not really going to change the external behaviour.

It does seem a bit odd that the C client library continues to attempt to reconnect even after it knows that previous reconnects have failed. I might take a look deeper into that code to see if there's a reason for it, but that appears that is the way it goes today.

ravellan commented 5 years ago

Thanks for the reply. Now I'm getting really curious if this is intended behavior or some kind of bug/regression. Seems particularly odd if you consider the default setting for MQReconnectTimeout is 30 mins. Anyway if it gets cleaned up in the lib I'll just let the connection die out and not worry about it afterwards.

If one could ask for another improvement is the ability to register an event callback directly on the HCONN without an open object. Something like: func (object *MQQueueManager) CB(goOperation int32, gocbd *MQCBD) I could of course do it myself and submit a PR if that is preferred.

ibmmqmet commented 5 years ago

As long as you call MQDISC it all seems to work out in the end even if the DISC fails. I looked at the real source code and I can see what's going on, and it doesn't seem to have ever changed - certainly not since V9.0. But I can't tell if it is what was intended!

Having the alternative CB sounds a good idea. I'll look at doing that for the next update.

ibmmqmet commented 4 years ago

qmgr CB variant included in latest commit