如何解决ConcurrentModificationException持久化Spring
我需要在Spring应用程序中使用drools持久性,但是我遇到了从不同线程并行访问多个kie会话的问题。
实际上,这种行为是意外的,有时我会出现 ConcurrentModificationException ,有时会出现 NullPointerException 。我写了一个小实验项目:https://github.com/sibmaks/drools_spring_persistance。
测试: singleSessionWritesTest 总是可以正常工作,因为我在顺序模式下处理一个kie会话,但是 multipleWritesTest 却出现错误。
框架版本:
流口水:7.43.1。最终
春季:1.5.8。发布
P.S。代码段
Bean配置:
//...
@Configuration
public class DroolsConfig {
//...
@Bean
@Qualifier("droolsEnvironment")
public Environment droolsEnvironment(
@Qualifier("droolsDataSource") DataSource dataSource,//@Qualifier("drools-entityManagerFactory")
// LocalContainerEntityManagerfactorybean droolsEntityManagerFactory,KieServices kieServices) {
LocalContainerEntityManagerfactorybean droolsEntityManagerFactory = new LocalContainerEntityManagerfactorybean();
droolsEntityManagerFactory.setDataSource(dataSource);
droolsEntityManagerFactory.setPersistenceUnitName("drools.cookbook.persistence.jpa");
droolsEntityManagerFactory.afterPropertiesSet();
JpaTransactionManager jpaTransactionManager = new JpaTransactionManager(droolsEntityManagerFactory.getobject());
jpaTransactionManager.setnestedTransactionAllowed(false);
jpaTransactionManager.afterPropertiesSet();
TransactionManager transactionManager = new KieSpringTransactionManager(jpaTransactionManager);
Environment environment = kieServices.newenvironment();
environment.set(EnvironmentName.ENTITY_MANAGER_FACTORY,droolsEntityManagerFactory.getobject());
environment.set(EnvironmentName.PERSISTENCE_CONTEXT_MANAGER,new KieSpringJpaManager(environment));
environment.set(EnvironmentName.TRANSACTION_MANAGER,transactionManager);
environment.set(EnvironmentName.GLOBALS,new MapGlobalResolver());
return environment;
}
/...
}
protected KieSessionContainer getorCreate(String externalId) {
if (!kieSessionMap.containsKey(externalId)) {
creationLock.lock();
try {
if (!kieSessionMap.containsKey(externalId)) {
ClientKieSession clientKieSession = clientKieSessionRepository.findFirstByClientId(externalId);
KieSession kieSession;
if (clientKieSession == null) {
log.info("**** Create session for client: {}",externalId);
kieSession = droolsService.createSession(externalId);
} else {
log.info("**** Restore session for client: {}",externalId);
try {
kieSession = droolsService.loadSession(clientKieSession.getSessionIdentifier());
} catch (Exception e) {
log.error("**** Restore session for error,create new session",e);
clientKieSessionRepository.delete(clientKieSession);
return droolsService.getorCreate(externalId);
}
}
kieSession.addEventListener(new EventHandler());
kieSessionMap.put(externalId,new KieSessionContainer(kieSession,new reentrantlock()));
}
} finally {
creationLock.unlock();
}
}
return kieSessionMap.get(externalId);
}
@Transactional
protected KieSession createSession(String externalId) {
KieSession kieSession = kieStoreServices.newKieSession(kieBase,kieSessionConfiguration,environment);
ClientKieSession clientKieSession = new ClientKieSession();
clientKieSession.setSessionIdentifier(kieSession.getIdentifier());
clientKieSession.setClientId(externalId);
clientKieSession.setCreated(Timestamp.valueOf(LocalDateTime.Now()));
clientKieSessionRepository.save(clientKieSession);
return kieSession;
}
@Transactional
protected KieSession loadSession(long identifier) {
return kieStoreServices.loadKieSession(identifier,kieBase,environment);
}
实际插入代码(产生异常)
@Transactional
protected void processFacts(KieSession kieSession,Object ... facts) {
for(Object fact : facts) {
kieSession.execute(CommandFactory.newInsert(fact));
}
kieSession.fireAllRules();
}
测试代码:
Random random = new Random();
Executor executor = Executors.newFixedThreadPool(16);
List<String> ids = new ArrayList<>();
for(int i = 0; i < 16; i++) {
ids.add(UUID.randomUUID().toString());
}
AtomicInteger counter = new AtomicInteger();
AtomicInteger finished = new AtomicInteger();
long time = System.currentTimeMillis();
AtomicBoolean exception = new AtomicBoolean();
for(int i = 0; i < COUNT; i++) {
executor.execute(() -> {
if(exception.get()) {
return;
}
try {
droolsService.processFacts(ids.get(random.nextInt(ids.size())),new LongFact(counter.incrementAndGet()));
finished.incrementAndGet();
} catch (Exception e) {
log.error(e.getMessage(),e);
exception.set(true);
}
});
}
while (finished.get() != COUNT && !exception.get()) {
TimeUnit.MILLISECONDS.sleep(100);
}
long end = System.currentTimeMillis();
log.info("{} execution time. {} op per second",(end - time),(0.0 + end - time) / COUNT);
int result = 0;
int resultQ = 0;
for(String id : ids) {
result += droolsService.sessionDump(id).stream().filter(it -> it instanceof LongFact).count();
resultQ += droolsService.processQuery(id,"getLongs").size();
}
Assert.assertEquals(COUNT,result);
Assert.assertEquals(COUNT,resultQ);
例外示例:
java.util.ConcurrentModificationException: null
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909) ~[na:1.8.0_191]
at java.util.ArrayList$Itr.next(ArrayList.java:859) ~[na:1.8.0_191]
at java.util.Collections$UnmodifiableCollection$1.next(Collections.java:1042) ~[na:1.8.0_191]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:580) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1300) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_191]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_191]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_191]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_191]
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347) ~[spring-orm-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at com.sun.proxy.$Proxy115.flush(UnkNown Source) ~[na:na]
at org.drools.persistence.TriggerUpdateTransactionSynchronization.beforeCompletion(TriggerUpdateTransactionSynchronization.java:76) ~[drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.kie.spring.persistence.SpringTransactionSynchronizationAdapter.beforeCompletion(SpringTransactionSynchronizationAdapter.java:54) ~[kie-spring-7.43.1.Final.jar:7.43.1.Final]
at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCompletion(TransactionSynchronizationUtils.java:106) ~[spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCompletion(AbstractPlatformTransactionManager.java:945) [spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:745) [spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) [spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.kie.spring.persistence.KieSpringTransactionManager.commit(KieSpringTransactionManager.java:70) [kie-spring-7.43.1.Final.jar:7.43.1.Final]
at org.drools.persistence.PersistableRunner$TransactionInterceptor.execute(PersistableRunner.java:608) [drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.drools.persistence.PersistableRunner$TransactionInterceptor.execute(PersistableRunner.java:565) [drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.drools.persistence.PersistableRunner.execute(PersistableRunner.java:400) [drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.drools.persistence.PersistableRunner.execute(PersistableRunner.java:68) [drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.drools.core.runtime.InternalLocalRunner.execute(InternalLocalRunner.java:37) [drools-core-7.43.1.Final.jar:7.43.1.Final]
at org.drools.core.runtime.InternalLocalRunner.execute(InternalLocalRunner.java:41) [drools-core-7.43.1.Final.jar:7.43.1.Final]
at org.drools.core.command.impl.CommandBasedStatefulKNowledgeSession.execute(CommandBasedStatefulKNowledgeSession.java:537) [drools-core-7.43.1.Final.jar:7.43.1.Final]
at org.example.drools_persistance_spring.service.DroolsService.processFacts(DroolsService.java:74) [main/:na]
at org.example.drools_persistance_spring.service.DroolsService.processFacts(DroolsService.java:64) [main/:na]
at org.example.drools_persistance_spring.service.DroolsServiceTest.lambda$multipleWritesTest$2(DroolsServiceTest.java:84) [test/:na]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_191]
另一个异常(重启测试后)
javax.persistence.OptimisticLockException: Batch update returned unexpected row count from update [0]; actual row count: -1; expected: 1
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.wrapStaleStateException(AbstractEntityManagerImpl.java:1729) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1634) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1602) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1608) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1303) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_191]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_191]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_191]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_191]
at org.springframework.orm.jpa.ExtendedEntityManagerCreator$ExtendedEntityManagerInvocationHandler.invoke(ExtendedEntityManagerCreator.java:347) ~[spring-orm-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at com.sun.proxy.$Proxy115.flush(UnkNown Source) ~[na:na]
at org.drools.persistence.TriggerUpdateTransactionSynchronization.beforeCompletion(TriggerUpdateTransactionSynchronization.java:76) ~[drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.kie.spring.persistence.SpringTransactionSynchronizationAdapter.beforeCompletion(SpringTransactionSynchronizationAdapter.java:54) ~[kie-spring-7.43.1.Final.jar:7.43.1.Final]
at org.springframework.transaction.support.TransactionSynchronizationUtils.triggerBeforeCompletion(TransactionSynchronizationUtils.java:106) ~[spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.triggerBeforeCompletion(AbstractPlatformTransactionManager.java:945) [spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:745) [spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:730) [spring-tx-4.3.12.RELEASE.jar:4.3.12.RELEASE]
at org.kie.spring.persistence.KieSpringTransactionManager.commit(KieSpringTransactionManager.java:70) [kie-spring-7.43.1.Final.jar:7.43.1.Final]
at org.drools.persistence.PersistableRunner$TransactionInterceptor.execute(PersistableRunner.java:608) [drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.drools.persistence.PersistableRunner$TransactionInterceptor.execute(PersistableRunner.java:565) [drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.drools.persistence.PersistableRunner.execute(PersistableRunner.java:400) [drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.drools.persistence.PersistableRunner.execute(PersistableRunner.java:68) [drools-persistence-jpa-7.43.1.Final.jar:7.43.1.Final]
at org.drools.core.runtime.InternalLocalRunner.execute(InternalLocalRunner.java:37) [drools-core-7.43.1.Final.jar:7.43.1.Final]
at org.drools.core.runtime.InternalLocalRunner.execute(InternalLocalRunner.java:41) [drools-core-7.43.1.Final.jar:7.43.1.Final]
at org.drools.core.command.impl.CommandBasedStatefulKNowledgeSession.execute(CommandBasedStatefulKNowledgeSession.java:537) [drools-core-7.43.1.Final.jar:7.43.1.Final]
at org.example.drools_persistance_spring.service.DroolsService.processFacts(DroolsService.java:74) [main/:na]
at org.example.drools_persistance_spring.service.DroolsService.processFacts(DroolsService.java:64) [main/:na]
at org.example.drools_persistance_spring.service.DroolsServiceTest.lambda$multipleWritesTest$2(DroolsServiceTest.java:84) [test/:na]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) ~[na:1.8.0_191]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) ~[na:1.8.0_191]
at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_191]
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: -1; expected: 1
at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:67) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:54) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.engine.jdbc.batch.internal.NonBatchingBatch.addToBatch(NonBatchingBatch.java:46) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3134) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:3013) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:3393) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.action.internal.EntityUpdateAction.execute(EntityUpdateAction.java:145) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:582) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.engine.spi.ActionQueue.executeActions(ActionQueue.java:456) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.event.internal.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:337) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.event.internal.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:39) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.internal.SessionImpl.flush(SessionImpl.java:1282) ~[hibernate-core-5.0.12.Final.jar:5.0.12.Final]
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:1300) ~[hibernate-entitymanager-5.0.12.Final.jar:5.0.12.Final]
... 26 common frames omitted
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。