question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

New Programming Model of TransactionMQProducer

See original GitHub issue

Under current design, LocalTransaction is executed after half message is successfully sent. A LocalTransaction may takes long time to complete, and while it is running , TransactionCheckerListener may be called to report the status of the transaction. It’s very hard or even impossible for the TransactionCheckerListener to distinguish whether the transaction is on-going or is rollbacked.

I’d propose a new programming model of TransactionMQProducer.

  1. Start local transaction
  2. Execute local transaction logic
  3. Call TransactionMQProducer to send half message and record SendResult
  4. Commit or Rollback local transaction based on SendResult.
  5. Tell MQ the local transaction state.

According to this design, there is no extra work to do after half message is sent, TransactionCheckerListener can be confident the transaction is rollbacked if it can find a record in the database. And Step 5 can be easily implemented using Spring Transaction bound event.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:10 (2 by maintainers)

github_iconTop GitHub Comments

5reactions
duhengluckycommented, Mar 19, 2020
0reactions
diwayoucommented, Feb 9, 2021

https://github.com/openmessaging/openmessaging-java/blob/master/openmessaging-api/src/main/java/io/openmessaging/producer/Producer.java The API shown below is provided in OpenMessaging, any comments are welcome: )

image

image

I didn’t find the prepare method implementation in the code https://github.com/apache/rocketmq/blob/master/openmessaging/src/main/java/io/openmessaging/rocketmq/producer/ProducerImpl.java, rocketmq have implemented this? i implement a demo with spring transaction manager like ` @Slf4j public class TransactionProducer {

private RocketMQTemplate rocketMQTemplate;

private DefaultMQProducerImpl producer;

private MessageConverter messageConverter;

public TransactionProducer(RocketMQTemplate rocketMQTemplate) {
    this.rocketMQTemplate = rocketMQTemplate;

    Field field = FieldUtils.getField(TransactionMQProducer.class, "defaultMQProducerImpl", true);

    this.producer = (DefaultMQProducerImpl) ReflectionUtils.getField(field, rocketMQTemplate.getProducer());
    if (this.producer == null) {
        throw new RuntimeException("不能发送事务消息");
    }

    this.messageConverter = rocketMQTemplate.getMessageConverter();
}

public SendResult send(String destination, org.springframework.messaging.Message<?> rocketMessage) throws MQClientException {
    Message msg = RocketMQUtil.convertToRocketMessage(messageConverter,
            StandardCharsets.UTF_8.toString(),
            destination, Objects.requireNonNull(messageConverter.toMessage(rocketMessage.getPayload(), rocketMessage.getHeaders())));
    // ignore DelayTimeLevel parameter
    if (msg.getDelayTimeLevel() != 0) {
        MessageAccessor.clearProperty(msg, MessageConst.PROPERTY_DELAY_TIME_LEVEL);
    }

    Validators.checkMessage(msg, producer.getDefaultMQProducer());

    SendResult sendResult;
    MessageAccessor.putProperty(msg, MessageConst.PROPERTY_TRANSACTION_PREPARED, "true");
    MessageAccessor.putProperty(msg, MessageConst.PROPERTY_PRODUCER_GROUP, producer.getDefaultMQProducer().getProducerGroup());
    try {
        sendResult = producer.send(msg);
    } catch (Exception e) {
        throw new MQClientException("send message Exception", e);
    }

    LocalTransactionState localTransactionState = LocalTransactionState.UNKNOW;
    switch (sendResult.getSendStatus()) {
        case FLUSH_DISK_TIMEOUT:
        case FLUSH_SLAVE_TIMEOUT:
        case SLAVE_NOT_AVAILABLE:
            localTransactionState = LocalTransactionState.ROLLBACK_MESSAGE;
            break;
        default:
            break;
    }

    final org.apache.rocketmq.client.producer.SendResult finalSendResult = sendResult;
    TransactionSynchronization transactionSynchronization = new TransactionSynchronizationAdapter() {
        @Override
        public void afterCompletion(int status) {
            LocalTransactionState localState;
            if (status == TransactionSynchronization.STATUS_ROLLED_BACK){
                localState = LocalTransactionState.ROLLBACK_MESSAGE;
            } else if (status == TransactionSynchronization.STATUS_UNKNOWN) {
                localState = LocalTransactionState.UNKNOW;
            } else {
                localState = LocalTransactionState.COMMIT_MESSAGE;
            }

            try {
                producer.endTransaction(finalSendResult, localState, null);
            } catch (Exception e) {
                log.warn("local transaction execute " + localState + ", but end broker transaction failed", e);
            }
        }
    };

    TransactionSynchronizationManager.registerSynchronization(transactionSynchronization);

    if (localTransactionState.equals(LocalTransactionState.ROLLBACK_MESSAGE)) {
        throw new RuntimeException("rollback message because send mq fail");
    }

    TransactionSendResult transactionSendResult = new TransactionSendResult();
    transactionSendResult.setSendStatus(sendResult.getSendStatus());
    transactionSendResult.setMessageQueue(sendResult.getMessageQueue());
    transactionSendResult.setMsgId(sendResult.getMsgId());
    transactionSendResult.setQueueOffset(sendResult.getQueueOffset());
    transactionSendResult.setTransactionId(sendResult.getTransactionId());
    transactionSendResult.setLocalTransactionState(localTransactionState);
    
    return transactionSendResult;
}

} `

Read more comments on GitHub >

github_iconTop Results From Across the Web

Enabling Message Tracing - 华为云
Set enableMsgTrace of the constructor to true. For example: TransactionMQProducer producer = new TransactionMQProducer(null, "ProducerGroupName" ...
Read more >
com.alibaba.rocketmq.client.producer.TransactionMQProducer.start ...
This exception is thrown when a program attempts to create an URL from an incorrect specification. URLConnection (java.net). A connection to a URL...
Read more >
org.apache.rocketmq.client.exception.MQClientException
This page shows Java code examples of org.apache.rocketmq.client.exception.MQClientException.
Read more >
Use _local_execute in fMBT With Examples | LambdaTest
Learn how to use _local_execute function in fMBT framework for your next python automation project with LambdaTest Automation Testing Advisor.
Read more >
Diff - rocketmq-client-python - Google Git
diff --git a/.coveragerc b/.coveragerc new file mode 100644 index ... _handle)) + + +class TransactionMQProducer(Producer): + def __init__(self, group_id, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found