Like many things I do, it all started with an easy problem. I had an application which subscribed to a topic, it published on a topic string, then got the published message. Another application subscribed on the same topic string but did not get the message. I'll leave clues for the experts to spot as I explain the progress I made.
The first complication was the subscribing and publishing application was in Python on z/OS (which runs in ASCII), and the other application was written in C using the default EBCDIC. I had to spend time working out how to convert from ASCII to EBCDIC. (When using CHARV you can specify a string and its code page and have MQ do it for you automatically - easy once you know which code page to use.) For other strings I had to convert manually using the A2E function from C run time.
There are some commands for displaying status of pub sub, it took a little while to understand them and give me the data I wanted.
For example
My application did a subscription with topic string "/currency/rate/EUR/GBP".
The command gave
DIS TPSTATUS('/currency/rate/EUR/GBP')
CSQM297I ... CSQMDRTC NO TPSTATUS FOUND MATCHING REQUEST CRITERIA
Once I had opened the topic using the topic string, I got
CSQM201I ... CSQMDRTC DIS TPSTATUS DETAILS
TPSTATUS(/currency/rate/EUR/GBP)
TYPE(TOPIC)
DEFPRESP(SYNC)
PUB(ENABLED)
SUB(ENABLED)
ADMIN()
RETAINED(NO)
PUBCOUNT(0)
SUBCOUNT(1)
PUBSCOPE(ALL)
SUBSCOPE(ALL)
...
I set up the subscription using Python
topic_string = '/currency/rate/EUR/GBP'
sub_desc = pymqi.SD() # create the basic subscription
sub_desc['Options'] = pymqi.CMQC.MQSO_CREATE + pymqi.CMQC.MQSO_RESUME +
pymqi.CMQC.MQSO_DURABLE + pymqi.CMQC.MQSO_MANAGED
sub_desc.set_vs('SubName', "MYSUB" )
sub_desc.set_vs('ObjectString', topic_string)
The code to get the subscribed message was
get_opts = pymqi.GMO(
MsgHandle=hMsg.msg_handle,
Options=pymqi.CMQC.MQGMO_NO_SYNCPOINT +
pymqi.CMQC.MQGMO_CONVERT +
pymqi.CMQC.MQGMO_PROPERTIES_IN_HANDLE +
pymqi.CMQC.MQGMO_FAIL_IF_QUIESCING +
pymqi.CMQC.MQGMO_WAIT)
get_opts['WaitInterval'] = 1000
data = sub.get(None, pymqi.md(), get_opts)
print('Received data: [%s]' % data)
The code to publish to a topic was
msg = '1.3961'
topic = pymqi.Topic(qmgr, topic_string=topic_string)
topic.open(open_opts=pymqi.CMQC.MQOO_OUTPUT)
topic.close()
The Python program was subscribe, publish to topic, get the message.
When I ran the code, no messages were published. I tried changing the topic_string to '/currency/rate/EUR/USD', that had no effect. Eventually I tracked down a code page problem in my code; I fixed that - but the program still did not publish a message.
The more knowledgable people reading this may have spotted one problem.
The first time the program ran, the subscription code created a SUB(MYSUB) with the given topic string ('/currency/rate/EUR/USD'). When I changed the topic string to '/currency/rate/EUR/GBP', and published a message, the subscription was using the original topic string with USD!, but publishing to GBP. Any changes to the SUBscription are ignored unless MQSO_ALTER is specified.
I deleted the SUB and reran my application. Displaying the subscription (MYSUB) gave
CSQM201I %CSQ9 CSQMDRTC DIS SUB DETAILS 934
SUB(MYSUB)
SUBID(C3E2D8D4C3E2D8F94040404040404040DAFEED99504CE540)
DURABLE(YES)
SUBTYPE(API)
TOPICSTR(/currency/rate/EUR/GBP)
SUBLEVEL(1)
...
I can display the topic string and see what is subscribed to it
dis tpstatus('/currency/rate/EUR/GBP') type(sub)
This gave two subscriptions:
TPSTATUS(/currency/rate/EUR/GBP)
TYPE(SUB)
SUBID(C3E2D8D4C3E2D8F94040404040404040DAFE062AED26E2C0)
DURABLE(YES)
SUBTYPE(ADMIN)
SUBUSER(IBMUSER)
...
NUMMSGS(12)
and
TPSTATUS(/currency/rate/EUR/GBP)
TYPE(SUB)
SUBID(C3E2D8D4C3E2D8F94040404040404040DAFC73649B3C7200)
DURABLE(YES)
SUBTYPE(ADMIN)
SUBUSER(IBMUSER)
...
NUMMSGS(0)
So one subscription has seen 12 messages, the other subscription has seen 0 messages. This does not look like a wrong topic string, as they are resported as part of the display tpstatus.
I could not find a way of displaying all the information about a subscription in one command. I had to use DIS TPSTATUS.. TYPE(SUB), then use cut and paste to issue the DIS SUB SUBID(....). (It would be much easier for the poor end users if there was an ALL option to display all this data in one go.)
For example
SUB(COLIN)
SUBID(C3E2D8D4C3E2D8F94040404040404040DAFC73649B3C7200)
DURABLE(YES)
SUBTYPE(ADMIN)
DISTYPE(RESOLVED)
TOPICSTR(/currency/rate/EUR/GBP)
DEST(CP0000)
DESTQMGR()
DESTCORL(C3E2D8D4C3E2D8F94040404040404040DAFC73649B3C7200)
TOPICOBJ()
PUBACCT(00000000000000000000000000000000000000000000000000000000…)
USERDATA(CZZZ)
SUBUSER(IBMUSER)
SUBLEVEL(1)
...
So we can see from this subscription the topic string being subscribed on, and the queue name being used.
This subscription had no messages published to it; see the DISPLAY TPSTATUS TYPE(SUB) field NUMMSGS. Those of you with a PhD in PubSub may have spotted the problem; it is very well hidden. It is all to do with PubLevel and SubLevel. (The documentation is very confusing, it implies a subscription can have more than one sublevel).
I think of it as logic within publish which says for each subscription
If SubLevel <= PubLevel then publish the message else skip the publish
- The subscription from the python code had SubLevel = 0. (This was latent bug - it should have been set to 1!
- The SubLevel from my DEFINE SUB command was the default 1.
- The PubLevel from the Python code was 0 - this was the problem.
So the publish to my manual DEFINE SUB was skipped, and the publish to the Python code worked because PubLevel = 0 matched SubLevel = 0
The message is being published - but it is not still not there.
When I restarted my queue manager and ran the test, the Python application got no message found, which was a surprise to me. When I reran it, with debug code enabled - the get worked, and carried on working all day. Next day the same problem occurred. Very strange - is the debug code affecting the messages ?
I then remembered the default syncpoint behaviour (one of those face palm moments). On midrange the default is to put out out sync-point, so the messages are immediately available. On z/OS the default is to put within sync-point. The messages are only visible after a commit, or the implicit commit at MQDISC. On z/OS the messages were in syncpoint, and there was no commit. When I ran my program the second time, it got the message from the first run, and so on!
I put the commit in - and it all worked.
Check list of what to check
Check you are subscribing to the correct topic string.
Plan carefully how you define your topic(strings).
If you want to have an application define a subscription, consider some defensive programming.
- MQSO_CREATE will create a subscription based on the specified data. If it already exists report an error.
- MQSO_CREATE + MQSO_RESUME will create a subscription based on the specified data. If it already exists it will not report an error. A second usage will not change the subscription.
- MQSO_CREATE + MQSO_ALTER will create a subscription based on the specified data. A second usage will update the subscription. This could be considered dangerous if you have multiple applications running concurrently changing the topic string or other parameters.
- MQSO_RESUME does not create a subscription, not updates it. it uses the information in the subscription object.
It would be safer for an Administrator to define the MQSUB, and the applications to refer to it. Do not allow application users to be able to define a subscription. They should use the pre-defined MQSUB and add the topic string to give more granular data. For example I could use a null object and have a topic string of /TOP/SECRET/DATA/SPIESIN/ENGLAND. If you haven't set up security properly then I could access the data.
Check the PubLevel and the SubLevel.
If you are not using this functionality set PubLevel to 9 and SubLevel to 1 ( the defaults).
You can DISPLAY SUB(....) SUBLEVEL to see the specified value.
The PubLevel is specified in the MQPMO.PubLevel. There is no display command for this.
Check the sync-point options.
You should always, always, always specify MQPMO_NO_SYNCPOINT, or MQPMO_SYNCPOINT and the commit. Do not let it default in case someone ports your code to or from z/OS. You can waste days hunting down this problem.
No comments:
Post a Comment