Using Savepoints
The AEP engine allows a microservice to create savepoints in a message handler and then roll back to a savepoint to undo changes made between the savepoint and the point of initiation of the rollback. Changes that are rolled back are state changes and outbound sends made by the microservice during that period. The following example illustrates how this is done
AepMessageSender sender;
AepEngine engine;
@EventHandler
public void onNewOrder(NewOrderMessage newOrder, MyAppState state) {
// create a savepoint
int beforeProcessing = engine.createTransactionSavePoint();
Customer customer = state.getCustomers().getCustomer(newOrder.getCustomerId());
// update customer order count
customer.setOrdersReceived(customer.getOrdersReceived() + 1);
// on the customer's thousandth order send them a promotion
if(customer.getOrdersReceived() == 1000) {
sender.sendMessage("customer-promotions", createPromotionMessage(customer, order));
}
Product product = state.getProducts(order.getProductId());
if(product.getItemsAvailable() > newOrder.getQuantity()) {
product.setItemsAvailable(product.getItemsAvailable() - newOrder.getQuantity());
messageSender.sendMessage("order-accepted", prepareOrderAccept(newOrder));
}
else {
//uh-oh, guess we shouldn't have sent that promotion!
engine.rollbackToSavepoint(beforeProcessing);
customer.setOrdersRejected(customer.getOrdersRejected() + 1);
messageSender.sendMessage("order-rejected", prepareOrderReject(newOrder));
}
}Breaking down the above example we see:
The microservice creates a savepoint at the beginning of its handler
It then updates the count of orders received for a customer and possibly sends out a promotional message.
Later, if the handler determines that there isn't enough inventory to satisfy the order it rolls back to the initial savepoint which:
Resets the customer's ordersReceivedCount to its previous value
Cancels the possible promotional message for the customer.
After the rollback, the handler then sends an order rejected message and increments a rejection count for the customer.
The net result of processing is thus an incremented orderRejectedCount for the customer and an order rejected message.
The sections below discuss savepoints and rollback in more depth.
Creating Savepoints
A microservice can create a savepoint at any point in a message handler by calling the AEP engine's createTransactionSavepoint()method. The returned savepoint number can then later be reused to rollback to the state at the time the savepoint was created.
Getting the current savepoint
A microservice can retrieve the current savepoint via the AEP engine's getTransactionSavepoint() method.
Rolling Back to a Savepoint
This AEP engine'srollbackToTransactionSavepoint() method rolls back state changes to AEP managed state and any outbound message since the given savepoint (including work in savepoints created after the specified savepoint). The rollback operation leaves the provided savepoint marker in place. For example, if the microservice calls rollback with a savepoint value of 1, a subsequent call to getTransactionSavepoint() will return 1. New work done after the rollback can thus be rolled back to the same point. Any savepoints after the provided savepoint are discarded. If rollback is called with a savepoint value of 1 when getTransactionSavepoint() is at 2, savepoint 2 is discarded.
It is worth noting that savepoint rollbacks do not rollback the actual processing of a message from the engine's standpoint, just the effects of its processing. If a handler rolls back all processing work (e.g. rollbackToTransactionSavepoint(0), the engine will still consider the message as successfully processed and acknowledge it.
In addition to the restrictions outlined below an attempt to roll back to a savepoint less than 0 or greater than the current savepoint will result in an IllegalStateException.
Rollback Errors
An EAepRollbackError thrown from this method indicates that there was an internal or runtime error performing the rollback. In this case, microservice event handlers must allow the error to be thrown back for Talon to handle as the microservice store may be in a corrupt state. If the AEP engine can recover by rolling back the entire transaction, the error will be handled according to theAppExceptionHandlingPolicy. Otherwise, the engine will stop with the EAepRollbackError.
Outbound Message Considerations
Outbound messages that are rolled back cannot be reused - the transfer of ownership to the AEP engine is preserved. Additionally, it should be noted that rollback does not rollback changes made to outbound messages' fields. When the engine is configured to dispose on send the engine may dispose of such messages during rollback, so microservices should not rely on messages transferred to the engine being valid post rollback.
State and Embedded Entity Considerations
Objects that were created since the savepoint that is rolled back may be discarded and cleared by the engine during rollback. Therefore microservices should not attempt to reuse any state objects created since the savepoint that was rolled back.
Savepoints and Adaptive Batching
When the engine is configured for adaptive batching, multiple inbound messages are grouped into a single transaction. Savepoints don't span multiple inbound messages. Instead, the processing effects of previous messages are fenced off from subsequent savepoints. Effectively, under the covers, the engine creates an internal savepoint for fully processed inbound messages and resets the application visible savepoint to 0 for subsequent messages.
Savepoints and Multiple Event Handlers
If there are multiple event handlers for a given event, savepoints do span those handlers meaning that a subsequent handler for an event can rollback work done by a previous handler. Microservices may create a savepoint via createTransactionSavepoint() at the beginning of message processing to avoid rolling back work done by another handler. This allows microservices that chain multiple event handlers together to perform processing a mechanism by which later processors in the chain can completely rollback work.
Savepoint Restrictions
Transaction savepoints operations (create, get, rollback) are only supported when:
the engine is configured with savepoints enabled i.e. the AEP engine's
getEnableTransactionSavepoints()method returnstruethe engine is backed by a store -
getStore()!= null- as the AepEngine relies on the store's transaction machinery to perform a rollback.called from within application event handlers i.e., only the engine's message processing thread may work with savepoints.
the engine is not configured for parallel cluster replication i.e. the AEP engine descriptor's
getReplicateInParallel()method returnsfalse.
If any of the above criteria is not met an IllegalStateException is thrown. In addition to the above restrictions a microservice must not use the following savepoint operations in the underlying store:
Savepoints and HA
State Replication
Savepoint operations can only be performed in a message handler. When using StateReplication this means that savepoint creation and rollbacks can only be done on a Primary instance.
Event Sourcing
When using EventSourcing, message handlers are invoked on a Backup instance or an instance recovering from a transaction log. It is crucial that a backup or recovering instance's behavior or it will lead to divergence in the application's outbound messages. This means that application logic on a backup must create the same savepoints as a primary and rollback based on the same criteria. For this reason, it is often preferable for an EventSourcing microservice that encounters an error to simply throw an exception from its event handler and let the inbound message's fate be governed by the AppExceptionHandlingPolicy.
Last updated

