0

I'm trying to implement a destructive reading from WebSphere, i.e. I read message that should be then immediately deleted from the queue.

I written the code that worked fine until I started to mess with messages. For example, the last one was "add one message, read it, wait on empty queue, then add two messages". In my scenario this program should read the first message, wait until something appears, and then read it too.

However, the problem is that I've got a situation where I'm stuck. I have a message in the queue but I can't read if with BROWSE nor with CURSOR. Here is my code:

MQEnvironment.UserId = _queueSettings.UserName;
MQEnvironment.Password = _queueSettings.Password;


var manager = new MQQueueManager(_queueSettings.QueueManagerName, _queueSettings.ChannelName, _queueSettings.ConnectionName);
var queue = manager.AccessQueue(_queueSettings.QueueName, MQC.MQOO_FAIL_IF_QUIESCING | MQC.MQOO_INPUT_SHARED | MQC.MQOO_BROWSE);

var browseFirstOptions = new MQGetMessageOptions { Options = MQC.MQGMO_BROWSE_FIRST };
var cursorOptions = new MQGetMessageOptions { Options = MQC.MQGMO_MSG_UNDER_CURSOR };

var currentOptions = browseFirstOptions;

while (!cancellationToken.IsCancellationRequested)
{
    var logger = _contextlessLogger.ForContext("requestId", Guid.NewGuid());
    try
    {
        var msg = new MQMessage();
        queue.Get(msg, currentOptions);

        if (currentOptions == browseFirstOptions)
        {
            currentOptions = cursorOptions;
            continue;
        }

        string messageText = msg.ReadString(msg.MessageLength);
        RunProcessingTask(logger, messageText);
    }
    catch (MQException ex) when (IsNoMessagesException(ex) && currentOptions != browseFirstOptions)
    {
        currentOptions = browseFirstOptions;
    }
    catch (MQException ex) when (IsNoMessagesException(ex))
    {
        const int sleepIntervalMs = 5000;
        _contextlessLogger.Information("No messages in the queue. Sleeping for {sleepIntervalMs}ms", sleepIntervalMs);
        await Task.Delay(sleepIntervalMs);
    }
    catch (Exception ex)
    {
        logger.Error(ex, "Unexpected error occured");
    }
}

private static bool IsNoMessagesException(MQException exception) =>
    exception.ReasonCode == MQC.MQRC_NO_MSG_AVAILABLE
    || exception.ReasonCode == MQC.MQRC_NO_MSG_UNDER_CURSOR;

I just get 2033 or 2034 while I can see in UI that there is a message.

How could it be done? Maybe I'm doing the wrong thing?


I added Java tag as well because their code doesn't differ at all, for example I used this code as reference.

Alex Zhukovskiy
  • 9,565
  • 11
  • 75
  • 151
  • Why do you want to browse then get? In your logic it would log each message twice? I think it would be better to get with SYNCPOINT, log it, then commit. To the point of the @DanielSteinmann's answer, run `DIS QSTATUS(NAME.OF.QUEUE) UNCOM` and see if the `UNCOM` is `NO` or a number, if it is a number the message is not committed so you would be unable to GET it. Note that MQEnvironment is not recommended since it is not thread safe, better to use a hash table of properties. – JoshMc May 08 '18 at 15:27
  • @JoshMc becasuse documentation says that when I access queue first time it's logical position is -1 rather than being on the first message. This is why i browse the queue first: because if have to set the cursor position to 0, and then read queue regularly. – Alex Zhukovskiy May 08 '18 at 20:45
  • You can do a get without a browse and it will get the "first" message on the queue. The meaning of first will depend on the message sequence delivery setting of the queue itself. The Default is PRIORITY, this means that things will be delivered based on the priority with higher priority messages delivered first (messages of the same PRIORITY will be delivered in FIFO order). If the setting is FIFO then all messages are delivered in FIFO order and message priority is ignored. – JoshMc May 08 '18 at 22:43
  • Take a look at the following directory under your MQ installation: `\tools\dotnet\samples\cs\base\SimpleGet\SimpleGet.cs`, this shows a simple get sample that does not use browse. – JoshMc May 08 '18 at 23:23
  • 1
    `tools\dotnet\samples\cs\base\SimpleXAGet\SimpleXAGet.cs` is a sample that uses .Net transactions and gets messages under syncpoint. – JoshMc May 08 '18 at 23:30
  • @JoshMc sorry, I don't have this folder. I only have: `$ ls bin inc instinfo.tsk java lib lib64 licenses mqpatch.dat msg READMES samp swidtag` – Alex Zhukovskiy May 10 '18 at 08:45
  • Is this a Windows C# app? Do you have a full MQ client installed on your windows machine? If so that directory would be present. – JoshMc May 10 '18 at 17:04
  • @JoshMc I've installed it via [this nuget](https://github.com/OpenSharp/NNugets/tree/master/WebSphereMqClient) which is basically bunch of dlls. I don't need 700mb installation in order to query server. However, today I found that I can't proceed without these examples so I had to install it. – Alex Zhukovskiy May 10 '18 at 20:43

1 Answers1

1

while I can see in UI that there is a message.

I assume you see current queue depth of 1.

If you put a message on a queue, IBM MQ increments current queue depth immediately, i.e. before you commit the transaction. But you can get the message only after the transaction commit.

Daniel Steinmann
  • 2,119
  • 2
  • 15
  • 25
  • Hmm. Thank you for idea, this code works fine if I call `AccessQueue` each time queue is emptied. Isn't there any better approach, do you know? – Alex Zhukovskiy May 08 '18 at 14:20
  • 1
    I guess for subsequent calls you have to use `MQGMO_BROWSE_NEXT`. But as @JoshMc pointed out, I would get the message in a transaction and after successful processing task completion commit the transaction. – Daniel Steinmann May 08 '18 at 15:57