2017-06-16 38 views
1

私たちは、一連のスレッドがデッドロックし、サーバーが動作しなくなった生産インシデントが発生しました。試して調べるために、私はバネのトランザクションの伝播が異なるいくつかのものをテストしました。間違いがなければREQUIRES_NEW伝播は既存のトランザクションが全くなければ2つの接続を開始します。これは正しいです??私はグーグルを試みたが、これに関する情報は見つからなかった。@Transactional(propagation = Propagation.REQUIRES_NEW)は2つのトランザクションを開きますか?

私はテストを行いました。ここではサンプルクラスです:

ここ
package test; 

import org.springframework.stereotype.Service; 
import org.springframework.transaction.annotation.Propagation; 
import org.springframework.transaction.annotation.Transactional; 

@Service 
public class TheService { 

    @Transactional(propagation=Propagation.REQUIRES_NEW) 
    public void doSomething() { 
     System.out.println("Here I am doing something."); 
    } 
} 

は私が作ったユニットテストです:

package test; 

import javax.annotation.Resource; 

import org.junit.Test; 
import org.springframework.test.context.ContextConfiguration; 
import org.springframework.test.context.junit4.AbstractTransactionalJUnit4SpringContextTests; 

@ContextConfiguration(locations = {"classpath:test.xml"}) 
public class TheServiceTest extends AbstractTransactionalJUnit4SpringContextTests { 

    @Resource 
    private TheService theService; 

    @Test 
    public void test() { 
     theService.doSomething(); 
    } 

} 

そして最後にではなく、少なくとも、ここに私のテストXMLは次のとおりです。

<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:context="http://www.springframework.org/schema/context" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xsi:schemaLocation=" 
    http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context-3.1.xsd 
    http://www.springframework.org/schema/tx 
    http://www.springframework.org/schema/tx/spring-tx-3.1.xsd" 
    default-autowire="byName"> 

    <context:component-scan base-package="test" /> 

    <tx:annotation-driven transaction-manager="transactionManager" /> 

    <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> 
     <property name="entityManagerFactory" ref="operator.entityManagerFactory" /> 
    </bean> 

    <bean id="operator.entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
     <property name="persistenceUnitName" value="operatorPersistenceUnit" /> 
     <property name="dataSource" ref="operator.dataSource" /> 
     <property name="jpaVendorAdapter"> 
      <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter"> 
       <property name="showSql" value="false" /> 
       <property name="generateDdl" value="true" /> 
       <property name="databasePlatform" value="org.hibernate.dialect.H2Dialect" /> 
      </bean> 
     </property> 
    </bean> 

    <bean id="operator.dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> 
     <property name="driverClassName" value="org.h2.Driver" /> 
     <property name="url" value="jdbc:h2:mem:operator" /> 
     <property name="username" value="sa" /> 
     <property name="password" value="" /> 
     <property name="maxActive" value="1" /> <!-- NOTE --> 
    </bean> 
</beans> 

理由私はREQUIRES_NEWメソッドのほうが重要です。なぜなら、そこからダーティーな読み込みを得ることが不可欠であり、別のトランザクションと外部の両方から実行できるからです。

maxActiveプロパティを1に保つと、このテストはデッドロックとなり、何も印刷されません。しかし、それを2に変更すると、テストは完了します。

maxActiveを非常に高い値に設定しても、このメソッドを実行するために十分なスレッドが待機していても、すべてが1つの接続を占有して2番目の接続を待つことができるからです。

私は間違ったことをしましたか?私は何かを誤解したことがありますか?

ご協力いただきありがとうございます。ありがとう!

+0

いいえ、2つの接続を開くことはできません。 'REQUIRES_NEW'のために、あなたのテストでトランザクションが開かれ、サービスで新しいものが1つ開かれたため、あなたのテストでは実行されます。 –

+0

なぜ私はテストでオープンしましたか?それを避ける方法はありますか?また、特定のメソッドがトランザクション内にあるかどうかを確認するために、新しいException()。printStackTrace()を追加し、TransactionInterceptorを探します。これは、テストメソッドに追加するときには見えません。 – user3306066

+0

'AbstractTransactionalJUnit4SpringContextTests'は、classnameの' Transactional'部分が理由のためにそこにあると思いますか?トランザクションを処理するには、さらに多くの方法があります。トランザクションを手動で開始/停止する 'TransactionalTestExecutionListener'によって行われるテストです。テストをトランザクションにしたくない場合は、 'AbstractTransactionalJUnit4SpringContextTests'を拡張しないでください。 –

答えて

2

propagation=REQUIRES_NEWとは、デフォルトで2つの接続を開くことはありません。問題はあなたがAbstractTransactionalJUnit4SpringContextTestsを拡張していることです。

あなたのテストケースはAbstractTransactionalJUnit4SpringContextTestsです。ご覧のとおり、@Transactionalです。このトランザクションは、テストのためにTransactionalTestExecutionListenerによって管理されます。

何が起こるかは、テストフレームワークによってトランザクションが開始されるテストメソッドの実行前に、テストを開始するときです。次に、@Transactional(propagation=REQUIRES_NEW)という注釈が付けられているため、別のトランザクションを開始するサービスを呼び出します。

修正は非常に簡単です。AbstractTransactionalJUnit4SpringContextTestsを拡張せず、クラスに注釈を付けて@RunWith(SpringRunner.class)としてください。

+0

あなたは絶対に正しいです。私の悪い。助けてくれてありがとう、良いサー! – user3306066

関連する問題