@@ -59,7 +59,7 @@ func TestAsyncPutSample(t *testing.T) {
59
59
}
60
60
61
61
/*
62
- * Compare the performance benefit of sending messages non-persistent, non-transational
62
+ * Compare the performance benefit of sending messages non-persistent, non-transactional
63
63
* messages asynchronously - which can give a higher message rate, in exchange for
64
64
* less/no checking for errors.
65
65
*
@@ -298,7 +298,7 @@ func TestAsyncPutCheckCountWithFailure(t *testing.T) {
298
298
// the next check which takes place at 30.
299
299
if i == 0 && errSend != nil && errSend .GetReason () == "MQRC_UNKNOWN_OBJECT_NAME" {
300
300
301
- fmt .Println ("Skipping TestAsyncPutCheckCountWithFailure as " + QUEUE_25_NAME + " is not defined." )
301
+ fmt .Println ("Skipping TestAsyncPutCheckCountWithFailure as queue " + QUEUE_25_NAME + " is not defined." )
302
302
queueExists = false
303
303
break // Stop the loop at this point as we know it won't change.
304
304
@@ -402,3 +402,344 @@ func TestAsyncPutGetterSetter(t *testing.T) {
402
402
assert .Equal (t , jms20subset .Destination_PUT_ASYNC_ALLOWED_AS_DEST , queue .GetPutAsyncAllowed ())
403
403
404
404
}
405
+
406
+ /*
407
+ * Compare the performance benefit of sending messages persistent, transactional
408
+ * messages asynchronously - which can give a higher message rate, in exchange for
409
+ * less/no checking for errors.
410
+ *
411
+ * The test checks that async put is at least 10% faster than synchronous put.
412
+ * (in testing against a remote queue manager it was actually 30% faster)
413
+ */
414
+ func TestAsyncPutPersistentTransactedComparison (t * testing.T ) {
415
+
416
+ // Loads CF parameters from connection_info.json and applicationApiKey.json in the Downloads directory
417
+ cf , cfErr := mqjms .CreateConnectionFactoryFromDefaultJSONFiles ()
418
+ assert .Nil (t , cfErr )
419
+
420
+ // Check the default value for SendCheckCount, which means never check for errors.
421
+ assert .Equal (t , 0 , cf .SendCheckCount )
422
+
423
+ // Creates a connection to the queue manager, using defer to close it automatically
424
+ // at the end of the function (if it was created successfully)
425
+ transactedContext , ctxErr := cf .CreateContextWithSessionMode (jms20subset .JMSContextSESSIONTRANSACTED )
426
+ assert .Nil (t , ctxErr )
427
+ if transactedContext != nil {
428
+ defer transactedContext .Close ()
429
+ }
430
+
431
+ // --------------------------------------------------------
432
+ // Start by sending a set of messages using the normal synchronous approach, in
433
+ // order that we can get a baseline timing.
434
+
435
+ // Set up the producer and consumer with the SYNCHRONOUS (not async yet) queue
436
+ syncQueue := transactedContext .CreateQueue ("DEV.QUEUE.1" )
437
+ producer := transactedContext .CreateProducer ().SetDeliveryMode (jms20subset .DeliveryMode_PERSISTENT ).SetTimeToLive (60000 )
438
+
439
+ consumer , errCons := transactedContext .CreateConsumer (syncQueue )
440
+ assert .Nil (t , errCons )
441
+ if consumer != nil {
442
+ defer consumer .Close ()
443
+ }
444
+
445
+ // Create a unique message prefix representing this execution of the test case.
446
+ testcasePrefix := strconv .FormatInt (currentTimeMillis (), 10 )
447
+ syncMsgPrefix := "syncput_" + testcasePrefix + "_"
448
+ asyncMsgPrefix := "asyncput_" + testcasePrefix + "_"
449
+ numberMessages := 50
450
+
451
+ // First get a baseline for how long it takes us to send the batch of messages
452
+ // WITHOUT async put (i.e. using normal synchronous put)
453
+ syncStartTime := currentTimeMillis ()
454
+ for i := 0 ; i < numberMessages ; i ++ {
455
+
456
+ // Create a TextMessage and send it.
457
+ msg := transactedContext .CreateTextMessageWithString (syncMsgPrefix + strconv .Itoa (i ))
458
+
459
+ errSend := producer .Send (syncQueue , msg )
460
+ assert .Nil (t , errSend )
461
+ }
462
+ transactedContext .Commit ()
463
+ syncEndTime := currentTimeMillis ()
464
+
465
+ syncSendTime := syncEndTime - syncStartTime
466
+ //fmt.Println("Took " + strconv.FormatInt(syncSendTime, 10) + "ms to send " + strconv.Itoa(numberMessages) + " transacted synchronous messages.")
467
+
468
+ // Receive the messages back again to tidy the queue back to a clean state
469
+ finishedReceiving := false
470
+ rcvCount := 0
471
+
472
+ for ! finishedReceiving {
473
+ rcvTxt , errRvc := consumer .ReceiveStringBodyNoWait ()
474
+ assert .Nil (t , errRvc )
475
+
476
+ if rcvTxt != nil {
477
+ // Check the message bod matches what we expect
478
+ assert .Equal (t , syncMsgPrefix + strconv .Itoa (rcvCount ), * rcvTxt )
479
+ rcvCount ++
480
+ } else {
481
+ finishedReceiving = true
482
+ }
483
+ }
484
+ transactedContext .Commit ()
485
+
486
+ // --------------------------------------------------------
487
+ // Now repeat the experiment but with ASYNC message put
488
+ asyncQueue := transactedContext .CreateQueue ("DEV.QUEUE.1" ).SetPutAsyncAllowed (jms20subset .Destination_PUT_ASYNC_ALLOWED_ENABLED )
489
+
490
+ asyncStartTime := currentTimeMillis ()
491
+ for i := 0 ; i < numberMessages ; i ++ {
492
+
493
+ // Create a TextMessage and send it.
494
+ msg := transactedContext .CreateTextMessageWithString (asyncMsgPrefix + strconv .Itoa (i ))
495
+
496
+ errSend := producer .Send (asyncQueue , msg )
497
+ assert .Nil (t , errSend )
498
+ }
499
+ transactedContext .Commit ()
500
+ asyncEndTime := currentTimeMillis ()
501
+
502
+ asyncSendTime := asyncEndTime - asyncStartTime
503
+ //fmt.Println("Took " + strconv.FormatInt(asyncSendTime, 10) + "ms to send " + strconv.Itoa(numberMessages) + " transacted ASYNC messages.")
504
+
505
+ // Receive the messages back again to tidy the queue back to a clean state
506
+ finishedReceiving = false
507
+ rcvCount = 0
508
+
509
+ for ! finishedReceiving {
510
+ rcvTxt , errRvc := consumer .ReceiveStringBodyNoWait ()
511
+ assert .Nil (t , errRvc )
512
+
513
+ if rcvTxt != nil {
514
+ // Check the message bod matches what we expect
515
+ assert .Equal (t , asyncMsgPrefix + strconv .Itoa (rcvCount ), * rcvTxt )
516
+ rcvCount ++
517
+ } else {
518
+ finishedReceiving = true
519
+ }
520
+ }
521
+ transactedContext .Commit ()
522
+
523
+ assert .Equal (t , numberMessages , rcvCount )
524
+
525
+ // Expect that async put is at least 10% faster than sync put for non-persistent messages
526
+ // (in testing against a remote queue manager it was actually 30% faster)
527
+ assert .True (t , 100 * asyncSendTime < 90 * syncSendTime )
528
+
529
+ }
530
+
531
+ /*
532
+ * Validate that errors are reported at the correct interval when a problem occurs during a
533
+ * transactional put of persistent messages (i.e. when the commit occurs)
534
+ *
535
+ * This test case forces a failure to occur by sending 50 messages to a queue that has had its
536
+ * maximum depth set to 25. In the transacted async put case the Send method always completes
537
+ * successfully, but the error will be returned on Commit.
538
+ */
539
+ func TestAsyncPutTransactedCheckCountWithFailure (t * testing.T ) {
540
+
541
+ // Loads CF parameters from connection_info.json and applicationApiKey.json in the Downloads directory
542
+ cf , cfErr := mqjms .CreateConnectionFactoryFromDefaultJSONFiles ()
543
+ assert .Nil (t , cfErr )
544
+
545
+ // The SendCheckCount is not used for async put under a transacted session.
546
+ assert .Equal (t , 0 , cf .SendCheckCount )
547
+
548
+ // Creates a connection to the queue manager, using defer to close it automatically
549
+ // at the end of the function (if it was created successfully)
550
+ transactedContext , ctxErr := cf .CreateContextWithSessionMode (jms20subset .JMSContextSESSIONTRANSACTED )
551
+ assert .Nil (t , ctxErr )
552
+ if transactedContext != nil {
553
+ defer transactedContext .Close ()
554
+ }
555
+
556
+ // Set up the producer and consumer with the async queue.
557
+ QUEUE_25_NAME := "DEV.MAXDEPTH25"
558
+ asyncQueue := transactedContext .CreateQueue (QUEUE_25_NAME ).SetPutAsyncAllowed (jms20subset .Destination_PUT_ASYNC_ALLOWED_ENABLED )
559
+ producer := transactedContext .CreateProducer ().SetDeliveryMode (jms20subset .DeliveryMode_PERSISTENT ).SetTimeToLive (60000 )
560
+
561
+ // Create a unique message prefix representing this execution of the test case.
562
+ testcasePrefix := strconv .FormatInt (currentTimeMillis (), 10 )
563
+ msgPrefix := "checkCount_" + testcasePrefix + "_"
564
+ numberMessages := 50
565
+
566
+ // Variable to track whether the queue exists or not.
567
+ queueExists := true
568
+
569
+ // --------------------------------------------------------
570
+ // Send ASYNC message put
571
+ for i := 0 ; i < numberMessages ; i ++ {
572
+
573
+ // Create a TextMessage and send it.
574
+ msg := transactedContext .CreateTextMessageWithString (msgPrefix + strconv .Itoa (i ))
575
+
576
+ errSend2 := producer .Send (asyncQueue , msg )
577
+
578
+ // Skip the test if the destination does not exist on this queue manager.
579
+ if i == 0 && errSend2 != nil && errSend2 .GetReason () == "MQRC_UNKNOWN_OBJECT_NAME" {
580
+
581
+ fmt .Println ("Skipping TestAsyncPutTransactedCheckCountWithFailure as queue " + QUEUE_25_NAME + " is not defined." )
582
+ queueExists = false
583
+ break // Stop the loop at this point as we know it won't change.
584
+
585
+ }
586
+
587
+ // In the Transacted case the response from Send is always Nil, because any errors
588
+ // will be reflected on the Commit call.
589
+ assert .Nil (t , errSend2 )
590
+
591
+ if i % 10 == 0 {
592
+ commitErr := transactedContext .Commit ()
593
+
594
+ if i == 30 || i == 40 {
595
+
596
+ assert .NotNil (t , commitErr )
597
+ assert .Equal (t , "2003" , commitErr .GetErrorCode ())
598
+ assert .Equal (t , "MQRC_BACKED_OUT" , commitErr .GetReason ())
599
+
600
+ // Linked error is out normal async put error with message about "N failures"
601
+ linkedErr1 := commitErr .GetLinkedError ()
602
+ assert .Equal (t , "AsyncPutFailure" , linkedErr1 .(jms20subset.JMSExceptionImpl ).GetErrorCode ())
603
+ assert .True (t , strings .Contains (linkedErr1 .(jms20subset.JMSExceptionImpl ).GetReason (), "6 failures" ))
604
+ assert .True (t , strings .Contains (linkedErr1 .(jms20subset.JMSExceptionImpl ).GetReason (), "0 warnings" ))
605
+
606
+ // Second level linked message should have reason of MQRC_Q_FULL
607
+ linkedErr2 := linkedErr1 .(jms20subset.JMSExceptionImpl ).GetLinkedError ()
608
+ assert .NotNil (t , linkedErr2 )
609
+ linkedReason := linkedErr2 .(jms20subset.JMSExceptionImpl ).GetReason ()
610
+ assert .Equal (t , "MQRC_Q_FULL" , linkedReason )
611
+
612
+ } else {
613
+ // Messages 31, 32, ... 39, 41, 42, ...
614
+ // do not give an error because we don't make an error check.
615
+ assert .Nil (t , commitErr )
616
+ }
617
+ }
618
+ }
619
+
620
+ // Clear out the transaction context.
621
+ transactedContext .Commit ()
622
+
623
+ // If the queue exists then tidy up the messages we sent.
624
+ if queueExists {
625
+
626
+ // ----------------------------------
627
+ // Receive the messages back again to tidy the queue back to a clean state
628
+ consumer , errCons := transactedContext .CreateConsumer (asyncQueue )
629
+ assert .Nil (t , errCons )
630
+ if consumer != nil {
631
+ defer consumer .Close ()
632
+ }
633
+
634
+ // Receive the messages back again
635
+ finishedReceiving := false
636
+
637
+ for ! finishedReceiving {
638
+ rcvMsg , errRvc := consumer .ReceiveNoWait ()
639
+ assert .Nil (t , errRvc )
640
+
641
+ if rcvMsg == nil {
642
+ finishedReceiving = true
643
+ }
644
+ }
645
+
646
+ transactedContext .Commit ()
647
+ }
648
+ }
649
+
650
+ /*
651
+ * Validate that NO errors are reported when a problem occurs during a
652
+ * transactional put of a non-persistent message.
653
+ *
654
+ * (per https://www.ibm.com/docs/en/ibm-mq/9.1?topic=application-putting-messages-asynchronously-in-mq-classes-jms)
655
+ *
656
+ * This test case forces a failure to occur by sending 50 messages to a queue that has had its
657
+ * maximum depth set to 25.
658
+ */
659
+ func TestAsyncPutTransactedNonPersistentCheckCountWithFailure (t * testing.T ) {
660
+
661
+ // Loads CF parameters from connection_info.json and applicationApiKey.json in the Downloads directory
662
+ cf , cfErr := mqjms .CreateConnectionFactoryFromDefaultJSONFiles ()
663
+ assert .Nil (t , cfErr )
664
+
665
+ // The SendCheckCount is not used for async put under a transacted session.
666
+ assert .Equal (t , 0 , cf .SendCheckCount )
667
+
668
+ // Creates a connection to the queue manager, using defer to close it automatically
669
+ // at the end of the function (if it was created successfully)
670
+ transactedContext , ctxErr := cf .CreateContextWithSessionMode (jms20subset .JMSContextSESSIONTRANSACTED )
671
+ assert .Nil (t , ctxErr )
672
+ if transactedContext != nil {
673
+ defer transactedContext .Close ()
674
+ }
675
+
676
+ // Set up the producer and consumer with the async queue.
677
+ QUEUE_25_NAME := "DEV.MAXDEPTH25"
678
+ asyncQueue := transactedContext .CreateQueue (QUEUE_25_NAME ).SetPutAsyncAllowed (jms20subset .Destination_PUT_ASYNC_ALLOWED_ENABLED )
679
+ producer := transactedContext .CreateProducer ().SetDeliveryMode (jms20subset .DeliveryMode_NON_PERSISTENT ).SetTimeToLive (60000 )
680
+
681
+ // Create a unique message prefix representing this execution of the test case.
682
+ testcasePrefix := strconv .FormatInt (currentTimeMillis (), 10 )
683
+ msgPrefix := "checkCount_" + testcasePrefix + "_"
684
+ numberMessages := 50
685
+
686
+ // Variable to track whether the queue exists or not.
687
+ queueExists := true
688
+
689
+ // --------------------------------------------------------
690
+ // Send ASYNC message put
691
+ for i := 0 ; i < numberMessages ; i ++ {
692
+
693
+ // Create a TextMessage and send it.
694
+ msg := transactedContext .CreateTextMessageWithString (msgPrefix + strconv .Itoa (i ))
695
+
696
+ errSend2 := producer .Send (asyncQueue , msg )
697
+
698
+ // Skip the test if the destination does not exist on this queue manager.
699
+ if i == 0 && errSend2 != nil && errSend2 .GetReason () == "MQRC_UNKNOWN_OBJECT_NAME" {
700
+
701
+ fmt .Println ("Skipping TestAsyncPutTransactedNonPersistentCheckCountWithFailure as queue " + QUEUE_25_NAME + " is not defined." )
702
+ queueExists = false
703
+ break // Stop the loop at this point as we know it won't change.
704
+
705
+ }
706
+
707
+ // In the Transacted case the response from Send is always Nil, because any errors
708
+ // will be reflected on the Commit call.
709
+ assert .Nil (t , errSend2 )
710
+
711
+ if i % 10 == 0 {
712
+ commitErr := transactedContext .Commit ()
713
+
714
+ // No errors reported for NonPersistent messages in a transaction.
715
+ assert .Nil (t , commitErr )
716
+
717
+ }
718
+ }
719
+
720
+ // If the queue exists then tidy up the messages we sent.
721
+ if queueExists {
722
+
723
+ // ----------------------------------
724
+ // Receive the messages back again to tidy the queue back to a clean state
725
+ consumer , errCons := transactedContext .CreateConsumer (asyncQueue )
726
+ assert .Nil (t , errCons )
727
+ if consumer != nil {
728
+ defer consumer .Close ()
729
+ }
730
+
731
+ // Receive the messages back again
732
+ finishedReceiving := false
733
+
734
+ for ! finishedReceiving {
735
+ rcvMsg , errRvc := consumer .ReceiveNoWait ()
736
+ assert .Nil (t , errRvc )
737
+
738
+ if rcvMsg == nil {
739
+ finishedReceiving = true
740
+ }
741
+ }
742
+
743
+ transactedContext .Commit ()
744
+ }
745
+ }
0 commit comments