into a separate back-end component, you preserve the availability of your customer-facing component. The lower availability of the message processor may be acceptable for business requirements.

Suppose, however, that 2PC is simply never acceptable in your system. How can this problem be solved? First, you need to understand the concept of idempotence. An operation is considered idempotent if it can be applied one time or multiple times with the same result. Idempotent operations are useful in that they permit partial failures, as applying them repeatedly does not change the final state of the system.

The selected example is problematic when looking for idempotence. Update operations are rarely idempotent. The example increments balance columns in place. Applying this operation more than once obviously will result in an incorrect balance. Even update operations that simply set a value, however, are not idempotent with regard to order of operations. If the system cannot guarantee that updates will be applied in the order they are received, the final state of the system will be incorrect.

More on this later.

In the case of balance updates, you need a way to track which updates have been applied successfully and which are still outstanding. One technique is to use a table that records the transaction identifiers that have been applied.

The table shown in figure 6 tracks the transaction ID, which balance has been updated, and the user ID where the balance was applied. Now our sample pseudocode is as shown in figure 7 .

This example depends
upon being able to peek a
message in the queue and
remove it once success-
f ully processed. This can
be
done with two indepen-
d ent
transactions if neces-
FIG 7 s ary: one on the message
q ueue
and one on the user
d atabase.
Queue operations
a re
not committed unless

U pdateTa ble updates_applied trans_id balance user_id

FIG 6

dequeue each message and apply the information to the user table. The example appears to solve all of the issues, but there is a problem. The message persistence is on the transaction host to avoid a 2PC during queuing. If the message is dequeued inside a transaction involving the user host, we still have a 2PC situation.

One solution to the 2PC in the message-processing component is to do nothing. By decoupling the update

B e gintransaction

Insert into transaction(id, seller_id, buyer_id, amount); Queue message “update user(“seller”, seller_id, amount)”; Queue message “update user(“buyer”, buyer_id, amount)”; E nd transaction

F or each message in queue Peek message

Begin transaction

Select count(*) as processed where trans_id=message.trans_id

and balance=message.balance and user_id=message.user_id

If processed == 0

If message.balance == “seller”

Update user set amt_sold=amt_sold + message.amount

where id=message.id; Else

Update user set amt_bought=amt_bought + message.amount

where id=message.id; End if

Insert into updates_applied

( message.trans_id, message.balance, message.user_id);

End if

End transaction

If transaction successful

Remove message from queue End if

References:

http://www.acmqueue.com

Archives