This topic describes how to use the message delaying feature of Message Service (MNS) to achieve the transactional consistency between on-premises operations and message sending.

Background information

The transactional consistency between on-premises operations and message sending is required in certain business scenarios. If a message is sent, an on-premises operation succeeds. If the message fails to be sent, the on-premises operation fails (a rollback is performed if the operation succeeds).

In addition, the message must be processed once by a consumer client. If consumer applications fail and the message fails to be processed, you need to manually reset the consumption progress.

Solution

Use the message delaying feature of MNS to solve the problem.

Prerequisites

The following queues are created:

  • Transactional message queue

    The message validity period is less than the message delay period in the queue. If producer clients do not modify and submit the validity period of messages, messages are invisible to consumer clients.

  • Operations log queue

    Stores operations logs of transactional messages. The message delay period is the timeout period of the transactional operation. If messages in the operations log queue are deleted, the messages are invisible to consumer clients.

Procedure

  1. Send a transactional message to the transactional message queue.
  2. A log entry that includes the receipt handle of the message is generated and written into the operations log queue.
  3. Implement the on-premises transaction.
  4. If Step 3 succeeds, the message is submitted and is visible to consumer clients. Otherwise, a rollback is performed.
  5. Confirm and delete the log entry that is generated in Step 2.
  6. After Step 4 is performed, consumer clients can receive the transactional message.
  7. A consumer client processes the message.
  8. The consumer client deletes the message.
The following figure shows the procedure of handling transactional messages.Transactional messages

Troubleshoot exceptions

  • Producer-side exceptions (for example, the process is restarted)
    1. The timeout occurs when you read the operations log queue and the log entry is not confirmed.
    2. Check whether the transaction is implemented.
    3. If the transaction is implemented, submit the message. Repeated message submission does not affect the system and a message with a unique receipt handle can be only submitted once.
    4. Confirm the log entry.
  • Consumer-side exceptions (for example, the process is restarted)
    • MNS ensures that a message is consumed at least once. If Step 8 fails, the message remains visible to the current or other consumer clients after a certain period.
    • MNS is unavailable. For example, the network is disconnected.

The MNS server stores the status and log entry of the message. MNS features high reliability and high availability, which allows you to continue the transaction after the network recovers. If the transactional operation succeeds, consumer clients can retrieve and process the message. Otherwise, consumer clients cannot retrieve the message.

Sample code

Java SDK 1.1.5 provides the TransactionQueue class to solve the problem. To download the Java SDK, click here. You only need to specify an on-premises transaction and the exception-handling logic in the following operations: TransactionOperations and TransactionChecker.

The sample code is provided as follows:

public class TransactionMessageDemo{
    public class MyTransactionChecker implements TransactionChecker
    {
        public boolean checkTransactionStatus(Message message)
        {
            boolean checkResult = false;
            String messageHandler = message.getReceiptHandle();
            try{
                //TODO: check if the messageHandler related transaction is success.
                checkResult = true;
            }catch(Exception e)
            {
                checkResult = false;
            }
            return checkResult;
        }
    }

    public class MyTransactionOperations implements TransactionOperations
    {
        public boolean doTransaction(Message message)
        {
            boolean transactionResult = false;
            String messageHandler = message.getReceiptHandle();
            String messageBody = message.getMessageBody();
            try{
                //TODO: do your local transaction according to the messageHandler and messageBody here.
                transactionResult = true;
            }catch(Exception e)
            {
                transactionResult = false;
            }
            return transactionResult;
        }
    }

    public static void main(String[] args) {
        System.out.println("Start TransactionMessageDemo");
        String transQueueName = "transQueueName";
        String accessKeyId = ServiceSettings.getMNSAccessKeyId();
        String accessKeySecret = ServiceSettings.getMNSAccessKeySecret();
        String endpoint = ServiceSettings.getMNSAccountEndpoint();

        CloudAccount account = new CloudAccount(accessKeyId, accessKeySecret, endpoint);
        MNSClient client = account.getMNSClient(); //this client need only initialize once

        // create queue for transaction queue.
        QueueMeta queueMeta = new QueueMeta();
        queueMeta.setQueueName(transQueueName);
        queueMeta.setPollingWaitSeconds(15);

        TransactionMessageDemo demo = new TransactionMessageDemo();
        TransactionChecker transChecker = demo.new MyTransactionChecker();
        TransactionOperations transOperations = demo.new MyTransactionOperations();

        TransactionQueue transQueue = client.createTransQueue(queueMeta, transChecker);

        // do transaction.
        Message msg = new Message();
        String messageBody = "TransactionMessageDemo";
        msg.setMessageBody(messageBody); 
        transQueue.sendTransMessage(msg, transOperations);

        // delete queue and close client if we won't use them.
        transQueue.delete();

        // close the client at the end.
        client.close();
        System.out.println("End TransactionMessageDemo");
    }

}