Subset of changes from transaction sometimes not v

2020-08-01 04:57发布

问题:

Let's consider the following context:

2 spring integration channels, they are each in separate database transactions. At the end of the first transaction, a message is put into the second channel. In the first channel, elements are created in the database that is later consumed by the corresponding message that has been sent from the first channel to the second channel.

To make sure that the transaction from channel 1 is fully committed before the second channel is triggered our subclass of the JpaTransactionManager is registering a TransactionSynchronization in the prepareForCommit method it overrides from the JpaTransactionManager

The flow (channel 1) looks like this:

  • Do all the message processing and database handling
  • Last step of the flow registers a TransactionSynchronization that does a MessageChannel.send in the afterCommit phase to send the message to channel 2

My understanding is that at the time the message is sent to the second channel (in afterCommit) all changes that have been done in the database transaction of channel 1 are flushed and committed.

Now the second channel does some work (like an MQ PUT) and later updates an entry that was created in the first flow. We have now observed that the repository method returned no entry in the database, but it is visible in the table at a later point. Other entries that were also created in the transaction of the first channel however are visible. This happens only once every few thousand messages, normally they are there but sometimes they are not visible for the second channel a few milliseconds after the transaction has been committed by channel 1.

I have created an image that should illustrate it:

The Chain 1 is the first chain that consists of multiple ServiceActivators that perform database work, a splitter that generates more messages and then another ServiceActivator that I named SENDER which registers the TransactionSynchronization that (so my understanding) should send the for example 3 generated messages to chain 2 after the red transaction is fully committed and therefore before the blue transaction begins.

One thing I have noticed is that the entries that were sometimes present and sometimes not are all in the one method that (not on purpose) uses javax.transaction.Transactional instead of org.springframework.transaction.annotation.Transactional. However, we are using spring core 5.0.8.RELEASE and in other questions I have seen that this should make 0 difference since spring 4.2.x.

回答1:

I don't think the afterCommit is the right place to send messages downstream.

There should be just enough to have a Service Activator for POJO method marked with the @Transactional. This way a transaction is going to start and finish exactly around this method call. The result of the method is going to be sent to the output channel already, exactly after that transaction is committed.

UPDATE

The best way to achieve your requirements is a <gateway> around your Chain1. This way the TX is going to be committed over there before producing reply to the Chain2 from the gateway.

With the TransactionSynchronization::afterCommit there is no guarantee that TX is going to be committed on DB when QueueChannel is ready for polling messages. Although you can use JdbcChannelMessageStore for transactional storage of messages. This way they are not going to be visible until TX commit in DB.

See more about <gateway> in Docs: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-routing-chapter.html#_calling_a_chain_from_within_a_chain