MQQueue.Get()
operation freezes for 5 minutes when getting a message from QueueManager using SSL on .NetCore 3.1. The same code works fine when running on .Net Framework (v4.8) or without SSL on both runtimes.
Details:
SSLCIPH(ANY_TLS12) SSLCAUTH(OPTIONAL)
My Findings:
MqGet-core31
tells:
MQRcvThread.ReceiveOneTSH()
to finish the get operation. It calls MQTCPConnection.Receive()
and it freeze for 5 minutes to 09:20:52.412669
MQTCPConnection
keeps original socket
and MQEncryptedSocket
instance wrapping the same socket
via SslStream
MQTCPConnection.Receive()
polls on the socket
and then it calls MQEncryptedSocket.Read()
socket
are read by SslStream
in MQEncryptedSocket
before the poll()
method is called and poll()
waits for its 5 minutes timeout. Or any other race condition around?poll()
call and delay it for a moment, then it often passess without any delay!Test Code:
This code finishes in ~2 seconds on .Net Framework, but it needs 5 minutes and ~2 seconds when it is run on .Net Core. (Win or Linux)
// import server's CA
MQConnectionPool.ImportServerCertificateAuthorityIfNotPresent(CertMqTestCa);
Hashtable properties = new Hashtable {
{MQC.HOST_NAME_PROPERTY, Mq1QmSslAnyTls12.Host},
{MQC.PORT_PROPERTY, Mq1QmSslAnyTls12.Port},
{MQC.CHANNEL_PROPERTY, Mq1QmSslAnyTls12.ChannelName},
{MQC.USER_ID_PROPERTY, "admin"},
{MQC.PASSWORD_PROPERTY, "changeit"},
{MQC.CCSID_PROPERTY, 819},
{MQC.SSL_CIPHER_SPEC_PROPERTY, "TLS_RSA_WITH_AES_256_CBC_SHA256"},
{MQC.SSL_CERT_STORE_PROPERTY, "*USER"}
};
MQQueueManager queueManager;
using (new MQCcsidSetter(properties)) {
queueManager = new MQQueueManager(null, properties);
}
string requestText = Guid.NewGuid().ToString();
string queueName = "SV.MqConnectionPoolTest";
// put message - works fine
using (MQQueue queue = queueManager.AccessQueue(queueName, MQC.MQOO_OUTPUT + MQC.MQOO_FAIL_IF_QUIESCING)) {
byte[] requestBytes = Encoding.UTF8.GetBytes(requestText);
MQMessage requestMessage = new MQMessage {Expiry = 3000, Priority = 4, CharacterSet = 1208, MessageType = MQC.MQMT_DATAGRAM};
requestMessage.Write(requestBytes, 0, requestBytes.GetLength(0));
queue.Put(requestMessage, new MQPutMessageOptions());
}
// get message back from the same queue
using (MQQueue queue = queueManager.AccessQueue(queueName, MQC.MQOO_INPUT_AS_Q_DEF + MQC.MQOO_FAIL_IF_QUIESCING)) {
while (true) {
MQMessage msg = new MQMessage();
queue.Get(msg, new MQGetMessageOptions()); // <<= !!!IT IS DELAYED HERE!!!
msg.Seek(0);
string msgContent = msg.ReadString(msg.MessageLength);
if (requestText.Equals(msgContent)) {
break;
}
}
}
Thread Dumps:
SocketPal.Poll()
Socket.Poll()
MQTCPConnection.Receive()
MQRcvThread.ReceiveBuffer()
MQRcvThread.ReceiveOneTSH()
MQRcvThread.Run()
ThreadHelper.ThreadStart_Context()
ExecutionContext.RunInternal()
ThreadHelper.ThreadStart()
Waits on Poll()
, but it ends with 5 minute (heartbeat) timeout. Then it goes to this.network.Read()
and immediately gets proper data.
this.socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, this.timeout);
if (this.socket.Poll(this.timeout * 1000, SelectMode.SelectRead))
{
length2 = this.network.Read(cBuffer, offset1, length1);
Monitor.Wait()
MQSession.ExchangeTSH()
MQProxyQueue.RequestMessages()
MQProxyQueue.FlushQueue()
MQProxyQueue.ProxyMQGET()
MQFAP.zstMQGET()
MQFAP.MQGET()
MQDestination.Get()
MQDestination.Get()
MqConnectionPoolTest.TestMqDirect()
Waits for entry.Reply
to be filled by receive thread:
while (entry.Reply == null) {
Monitor.Wait((object) entry, this.rmtReqEntMaxPollTime, true);
...
Please, does anybody know a workaround or has a fix?
You should not Get
and wait infinitely, you should do it like this:
const int TIMEOUTTIME = 20000;
try
{
MQGetMessageOptions gmo = new MQGetMessageOptions();
gmo.Options = MQC.MQGMO_WAIT;
gmo.WaitInterval = TIMEOUTTIME;
queue.Get(msg, gmo);
msg.Seek(0);
string msgContent = msg.ReadString(msg.MessageLength);
if (requestText.Equals(msgContent)) {
break;
}
}
catch (MQException ex)
{
if (ex.CompletionCode == MQC.MQCC_FAILED && ex.ReasonCode == MQC.MQRC_NO_MSG_AVAILABLE)
{
// Log on DEBUG level something like this:
// No message from <queue> after <TIMEOUTTIME / 1000> seconds, continue receiving");
continue;
}
throw ex;
}
To make your programmer's life easier, I would recommend to use the XMS API.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With