Sending events at the end of a transaction

I have an interface for a Service object that looks something like this (simplified for brevity):

public interface ItemService { public Item getItemById(String itemId, int version); public void create(Item item, User user); public void update(Item item, User user); public void delete(Item item, User user); } 

ItemService only implementation and connects as a Spring bean. It is used by part of the user interface of our project and by code that processes Ajax requests to create and modify Item objects in our data warehouse.

Under the hood, each method sends a series of events when called. Events are taken by other modules to do things like update Lucene indexes, or send messages to administrators so they know something has changed. Each method call represents a single transaction in Spring (using org.springframework.orm.hibernate3.HibernateTransactionManager and org.springframework.transaction.interceptor.TransactionProxyFactoryBean ).

Recently, it became necessary to compose a series of method calls in one transaction. Sometimes with multiple services. For example, we can do something like:

 *Begin transaction* Get Items created by User Bill using ItemService for each Item in Items Update field on Item Link Item to User Bill with LinkService Update Item using ItemService *Finish transaction* 

We did this by creating another Service, which allows you to make calls from the Services using one method in the parent service. Lets call it ComposingService . ComposingService , like everyone else, is also managed by Spring, and since transactions are reentrant, this should work.

However, a problem arises: if any of these operations in the transaction failed, forcing the transaction to cancel , we do not want to send any events at all .

However, if the transaction fails, half of the events will be dispatched by the ItemService before the transaction returns, which means that some modules will receive a bunch of events for things that haven't happened yet.

We are trying to find some way to fix this, but we could not come up with anything elegant. The best thing we've come up with so far is something like this (and it's ugly):

 public interface ItemService { public Item getItemById(String itemId, int version); public void create(Item item, User user, List<Event> events); public void update(Item item, User user, List<Event> events); public void delete(Item item, User user, List<Event> events); } 

In this modified ItemService, and not about events dispatched immediately, they are added to the list of events passed as an argument. The list is maintained by ComposingService , and events are dispatched by ComposingService after all calls to ItemService and other services have completed successfully.

Obviously, the problem is that we changed the contract to ItemService ugly. Calling classes, even if they are services, does not have to worry about event management. But I could not think about it, therefore this question.

This seems like a problem that was probably resolved earlier. Has anyone had a problem that looks similar, and if so, how did you resolve it?

+8
java spring design-patterns architecture
source share
3 answers

To summarize your question: you're looking for a transaction-safe way to send messages.

Option 1: JMS

Transactionally secure messaging is exactly what JMS is for. There is also good JMS integration in Spring, see Chapter

+20
source share

Unlike Wouter Coekaerts, I understand that you are asking for a way to send notifications that will only be sent to the recipient if the transaction in which they were created was successful. “So you are looking for something similar to the transactional mechanism of a CDI event.”

My idea is to solve it like this:

  • send events and "save" them in the list in your event processing engine, but do not forward events to the recipients. (I think it's easy to implement)
  • forward events from the list to recviers if the transaction was successful
  • remove events from the list if the transaction has a backup

To forward or delete events, I would first look at the spring transaction mechanism. If there is no way to extend it, you can write an AOP aspect that passes events if the method annotated with @Transactional leaves an exception (Runtime). But if the annotated @Transactional method left with a (Runtime) Exception, it removes events from the list.

+1
source share

As mentioned in @Wouter, one of the alternatives uses asynchronous message passing methods. I can think of two other approaches:

  • Events triggered using the ItemService are stored in the database table after committing (so they are not available when rolling back). Background (asynchronous) operation checks events and calls the corresponding services. You could call it a “homemade JMS”. Possible alternative if messaging infrastructure is not available.
  • Use ThreadLocal to temporarily pause all events and trigger events after complex transactions. This is not permanent and may not work, for example. after commit and before all events are canceled. I'm not a fan of ThreadLocal, but it's better than messing with business interfaces (ItemService) with unrelated parameters. See This for a spring related implementation .

I think that a final answer is impossible, because it really depends on the details of your system, especially on all external services caused by events.

+1
source share

All Articles