When Should You Send Messages in a Transaction? Real‑World Order Processing Insights

This article examines the trade‑offs of sending messages before, during, or after database persistence in order‑creation workflows, explores transaction‑message patterns, half‑message checks, and message‑table strategies, and offers practical guidance for building reliable backend messaging systems.

Alibaba Cloud Developer
Alibaba Cloud Developer
Alibaba Cloud Developer
When Should You Send Messages in a Transaction? Real‑World Order Processing Insights

The author reflects on the lack of a universal best practice for sending messages in business code, emphasizing the need for fault‑tolerant designs based on specific business contexts.

Problem Scenario

A developer in an order‑processing team faced uncertainty about where to place the message‑sending call in the transaction flow:

try {
  transactionTemplate.start(); // location 1
  orderManager.createOrder(order);
  // location 2
  messageProducer.send(buildOrderCreatedMsg(order)); // send order‑created message
  transactionTemplate.commit(); // location 3
} catch (Exception e) {
  transactionTemplate.rollback();
}

The question was whether to send the message at position 1, 2, or 3.

Analysis of the Three Positions

Send before persistence (position 1) : The order creation may take up to several seconds and can fail; sending a message here risks notifying a non‑existent order.

Send after business logic but before commit (position 2) : The order is created, but the transaction is not yet committed, so a failure or hang in the messaging path can block the commit.

Send after commit (position 3) : Guarantees the order exists, but occasional network errors or process restarts can cause lost messages, as observed in production.

Using Transactional Messages

To address these issues, the author adopted a transactional‑message approach:

try {
  transactionTemplate.start();
  messageTransactionProducer.send(buildOrderCreatedMsg(order), new TransactionExecutor(transactionTemplate){
    @Override
    public TransactionStatus execute(Message msg) {
      orderManager.createOrder(order);
      return TransactionStatus.CommitTransaction;
    }
  });
} catch (Exception e) {
  transactionTemplate.rollback();
}

// Half‑message check implementation
public class OrderTransactionCheckerImpl implements TransactionChecker {
  @Override
  public TransactionStatus check(Message msg) {
    if (orderManager.isOrderExist(getOrderId(msg))) {
      return TransactionStatus.CommitTransaction;
    } else {
      // If the check occurs within 20 s of sending, treat as unknown (transaction may still be pending)
      if (new Date().getTime() - msg.getSendTime() > 20000) {
        return TransactionStatus.RollbackTransaction;
      } else {
        return TransactionStatus.Unknown;
      }
    }
  }
}

Adding a 20‑second grace period for unknown status prevented premature rollbacks when the database was temporarily unavailable.

Message‑Table Alternative

Another simple solution is to write a record to a dedicated message table within the same transaction as the order:

try {
  transactionTemplate.start();
  orderManager.createOrder(order);
  miniBus.addMsg(buildOrderCreatedMsg(order)); // insert a half‑message record
  transactionTemplate.commit();
} catch (Exception e) {
  transactionTemplate.rollback();
}

A background scanner reads the table, processes pending messages, and deletes records after successful handling, ensuring strong consistency between business data and messages.

Key Takeaways

There is no single “best practice”; engineers must balance latency, failure handling, and system complexity. Important considerations include:

Ensuring order existence checks read from the primary database.

Defining acceptable message‑send timeouts and business reactions to large‑scale timeouts.

Assessing whether occasional message loss is tolerable and planning compensation mechanisms.

Verifying idempotency on the consumer side and the feasibility of bulk re‑delivery.

Ultimately, proactive fault‑tolerance design, rather than a silver‑bullet solution, is essential for stable message delivery.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

BackendtransactionMessage QueueReliabilityMessaging
Alibaba Cloud Developer
Written by

Alibaba Cloud Developer

Alibaba's official tech channel, featuring all of its technology innovations.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.