iComposer & AI
iComposer
iComposer Message Management
Sidebar On this page

Overview

iComposer Message Management is a distributed transactional messaging framework that enables asynchronous, reliable message delivery and execution within iComposer orchestration flows. Key capabilities include:

  • Asynchronous message scheduling and execution
  • Automatic retry and failure recovery
  • Distributed locking to ensure single-node processing in clustered environments
  • TCC transaction support (Seata integration)
  • Automatic archiving of completed messages to a history table
  • REST API for message querying, manual retry, and status reset

Architecture Overview

┌──────────────┐ TCC prepare ┌──────────────────────┐
Business ─────────────────► T_ICMP_TX_MESSAGE (current table)
Code status=NON_COMMIT(-1)
(Groovy API) └──────────┬───────────┘
└──────────────┘ TCC commit status=INITIAL(0)

┌──────────────────────┐
IComposerMessage
Scheduler (polling)
└──────────┬───────────┘
mark DOING(1)

┌──────────────────────┐
Thread Pool
GroovyRestfulExecutor
└──────────┬───────────┘
┌─────┴──────┐

SUCCESS(2) ERROR(3)

auto-retry (≤maxRetryTimes)

┌──────────────────────────┐
T_ICMP_TX_MESSAGE_HIS (history table)
└──────────────────────────┘

Message Status Lifecycle

Status CodeNameDescription
-1NON_COMMITTCC prepare phase; message reserved but not yet committed
0INITIALCommitted; waiting for scheduler to pick up and execute
1DOINGCurrently being processed
2SUCCESSExecuted successfully
3ERRORExecution failed; awaiting retry
4BLOCKBlocked; requires manual intervention

Status transition paths:

NON_COMMIT(-1) ──TCC commit──► INITIAL(0) ──scheduler──► DOING(1) ──success──► SUCCESS(2)
──fail───► ERROR(3) ──retry──► DOING(1)
──maxRetry──► BLOCK(4)
NON_COMMIT(-1) ──TCC rollback──► deleted
DOING(1) ──manual reset──► INITIAL(0)

Configuration

Configure the following parameters in application.properties or via environment variables:

ParameterDefaultDescription
icomposer.message.enabledNone (must be explicitly set to true)Enable the message scheduler
icomposer.message.cron0/5 * * * * ?Cron expression for message processing (default: every 5 seconds)
icomposer.message.backupCron0/10 * * * * ?Cron expression for message archiving (default: every 10 seconds)
icomposer.message.batchSize10Number of messages fetched per scheduling cycle
icomposer.message.schedulerLockTimeoutSeconds30Distributed lock timeout in seconds
icomposer.message.retryIntervalMinutes2Retry interval for failed messages in minutes
icomposer.message.maxRetryTimes5Maximum number of retry attempts
icomposer.message.backupBatchSize500Batch size for archiving messages
icomposer.message.useSystemUserfalseUse system user for execution (false = use message creator)

Minimal Configuration

# Enable message scheduler
icomposer.message.enabled=true

Production Tuning Example

icomposer.message.enabled=true
icomposer.message.cron=0/3 * * * * ?
icomposer.message.batchSize=20
icomposer.message.retryIntervalMinutes=5
icomposer.message.maxRetryTimes=10
icomposer.message.backupBatchSize=1000
icomposer.message.useSystemUser=false

REST API

All API paths are prefixed with /v1/flow/message.

1. Query Current Messages (Paginated)

POST /v1/flow/message/pagedQueryCurrent

Query messages from the current message table (T_ICMP_TX_MESSAGE).

Request Body:

{
"pageIndex": 1,
"pageSize": 20,
"businessNo": "POL-2025-001",
"functionName": "policyService",
"methodName": "issuePolicy",
"globalTransactionId": "tx-uuid-xxx",
"status": "3",
"traceId": "trace-xxx",
"submitTimeFrom": "2025-05-01T00:00:00",
"submitTimeTo": "2025-05-31T23:59:59",
"processTimeFrom": null,
"processTimeTo": null
}

Query Parameters:

FieldTypeRequiredDescription
pageIndexIntegerYesPage number (1-based)
pageSizeIntegerYesPage size
businessNoStringNoBusiness number (fuzzy match)
functionNameStringNoGroovy function name
methodNameStringNoGroovy method name
globalTransactionIdStringNoGlobal transaction ID
statusStringNoMessage status code
traceIdStringNoTrace ID
submitTimeFrom/ToLocalDateTimeNoSubmit time range
processTimeFrom/ToLocalDateTimeNoProcess time range
endTimeFrom/ToLocalDateTimeNoEnd time range

Response:

{
"data": [
{
"messageId": 1001,
"globalTransactionId": "tx-uuid-xxx",
"traceId": "trace-xxx",
"businessNo": "POL-2025-001",
"submitTime": "2025-05-20T10:30:00",
"processTime": "2025-05-20T10:30:05",
"endTime": null,
"transactionType": 1,
"isSuccessful": "N",
"errorMessage": "Connection timeout",
"functionName": "policyService",
"methodName": "issuePolicy",
"status": "3",
"redoTimes": 2,
"lastRedoTime": "2025-05-20T10:35:05"
}
],
"totalCount": 150,
"pageIndex": 1,
"pageSize": 20
}

2. Query Historical Messages (Paginated)

POST /v1/flow/message/pagedQuery

Query messages from the history archive table (T_ICMP_TX_MESSAGE_HIS). The request body and response format are identical to pagedQueryCurrent.

3. Retry Failed Messages

POST /v1/flow/message/redo

Re-queue failed messages from the history table back into the current message table for re-execution.

Request Body:

[1001, 1002, 1003]

A list of hisId values from the history table.

Response:

{
"code": "0",
"message": "success"
}

Note: The retry operation extracts message details from the history table, re-inserts them into the current message table with INITIAL(0) status, and the scheduler picks them up automatically.

4. Reset Message Status

POST /v1/flow/message/reset

Reset DOING(1) messages back to INITIAL(0). Used to recover messages stuck in DOING state due to node crashes or other failures.

Request Body:

[2001, 2002]

A list of messageId values from the current message table.

Response:

{
"code": "0",
"message": "success"
}

Use case: When messages remain in DOING status for an extended period (e.g., the executing node crashed), use this API to reset them to INITIAL so the scheduler can pick them up again.

Usage in Groovy Code

Use IComposerExecutorUtils.asyncExecuteWithMessage to submit asynchronous messages. This method internally uses the TCC pattern to ensure consistency between the message and the business transaction: the message takes effect when the outer transaction commits, and is automatically deleted on rollback.

Basic Usage

import com.insuremo.icomposer.utils.IComposerExecutorUtils

// Minimal form: businessNo + function + method + params
IComposerExecutorUtils.asyncExecuteWithMessage(
"POL-2025-001", // businessNo - business number (for querying and tracking)
"policyService", // serviceFunction - Groovy function name to invoke
"issuePolicy", // methodName - method name to execute
policyData // params - method parameters (varargs)
)

Specifying Transaction Type

// With custom txType (transaction type identifier, defaults to 100)
IComposerExecutorUtils.asyncExecuteWithMessage(
"POL-2025-001", // businessNo
200L, // txType - custom transaction type
"policyService", // serviceFunction
"issuePolicy", // methodName
policyData // params
)

Using Map Parameters

When there are many parameters, you can pass them as a Map (use LinkedHashMap to preserve parameter order):

// Map parameter form
IComposerExecutorUtils.asyncExecuteWithMessage(
"POL-2025-001", // businessNo
"policyService", // serviceFunction
"issuePolicy", // methodName
[
policyNo: "POL-2025-001",
productCode: "TRAVEL-PA",
insuredAmount: 100000
] // methodParamsMap - LinkedHashMap preserves order
)

// Map parameters + custom txType
IComposerExecutorUtils.asyncExecuteWithMessage(
"POL-2025-001", // businessNo
200L, // txType
"policyService", // serviceFunction
"issuePolicy", // methodName
[policyNo: "POL-2025-001", productCode: "TRAVEL-PA"]
)

Method Signatures

SignatureDescription
asyncExecuteWithMessage(businessNo, serviceFunction, methodName, Object... params)Basic form; txType defaults to 100
asyncExecuteWithMessage(businessNo, txType, serviceFunction, methodName, Object... params)Specify transaction type
asyncExecuteWithMessage(businessNo, serviceFunction, methodName, Map<String, Object> paramsMap)Map parameters; txType defaults to 100
asyncExecuteWithMessage(businessNo, txType, serviceFunction, methodName, Map<String, Object> paramsMap)Map parameters + custom transaction type

Tracing Message Execution

A traceId is automatically assigned during message execution. Use the traceId in your logging system to trace the full execution chain.

How the Scheduler Works

Message Processing Flow

  1. Scheduled polling: The scheduler triggers based on the cron configuration (default: every 5 seconds)
  2. Distributed lock: Acquires a non-reentrant lock to ensure only one node processes messages at a time
  3. Fetch pending messages:
    • Prioritizes INITIAL(0) status messages
    • If fewer than batchSize, supplements with ERROR(3) messages eligible for retry
  4. Mark as executing: Uses optimistic locking to change status from INITIAL/ERROR to DOING
  5. Thread pool execution: Submits messages to the thread pool for asynchronous Groovy function execution
  6. Record result: Updates status to SUCCESS or ERROR after execution, recording traceId and error details

Message Archiving Flow

  1. The scheduler triggers based on backupCron (default: every 10 seconds)
  2. Queries completed messages (SUCCESS and ERROR/BLOCK that have reached the retry limit)
  3. Batch-migrates them to the history table

Operations Guide

Common Scenarios

View Failed Messages

POST /v1/flow/message/pagedQueryCurrent
{
"pageIndex": 1,
"pageSize": 50,
"status": "3"
}

Manually Retry Failed Messages

  1. Find the hisId of messages to retry from the history table
  2. Call the redo API:
POST /v1/flow/message/redo
[1001, 1002]

Reset Stuck DOING Messages

When messages remain in DOING status due to a node crash:

POST /v1/flow/message/reset
[2001, 2002]

Track Messages by Business Number

POST /v1/flow/message/pagedQueryCurrent
{
"pageIndex": 1,
"pageSize": 20,
"businessNo": "POL-2025-001"
}

Monitoring Checklist

  • Watch for accumulation of ERROR and BLOCK status messages
  • Monitor thread pool queue size and active thread count
  • Watch for taskExecutor rejected warnings in scheduler logs
  • Check if distributed locks are timing out frequently

Important Notes

  • The scheduler is only activated when icomposer.message.enabled=true
  • Distributed locking is database-based, relying on the LockUtils utility
  • Message execution uses the message creator’s user context; falls back to ADMIN user if the user no longer exists
  • When icomposer.message.useSystemUser=true, all messages execute under the system user identity
  • Messages that reach maxRetryTimes are not automatically archived and require manual handling via the redo/reset APIs

Feedback
Was this page helpful?
|
Provide feedback