我org.springframework.orm.ObjectOptimisticLockingFailureException
在 Spring Boot 2.1 服务中得到了很多。我在 Spring's 有一份工作@Scheduled
,我在带有 Hibernate 的 PostgreSQL 中保存的地址不多。
延迟设置为fixedDelay = 5000L
, initialDelay = 5000L
。我使用net.javacrumbs.shedlock
,所以锁被持久化在数据库中,只有一个容器会获取锁并调用publishBatch()
。
它看起来像这样:
@Service
@RequiredArgsConstructor
public class AddressPublisher {
private static final Logger LOGGER = LoggerFactory.getLogger(AddressPublisher.class);
private static final int MAX_LOCK_IN_SECONDS = 600;
private final AddressRepository addressRepository;
private final AddressProducer addressProducer;
private final LockProvider lockProvider;
@Scheduled(fixedDelay = 5000L, initialDelay = 15000L)
public void publishModifiedAddresses() {
Optional<SimpleLock> lock = lockProvider.lock(lockConfigurationForPublishModifiedAddresses());
if (!lock.isPresent()) {
LOGGER.debug("did not acquire lock for publishing modified addresss");
return;
}
UUID jobUUID = UUID.randomUUID();
try(MDCConfig ignored = new MDCConfig(jobUUID)) {
do {
if (publishBatch()) break;
} while (true);
} finally {
lock.get().unlock();
}
}
@Transactional
public boolean publishBatch() {
List<Address> modifiedAddresses = addressRepository.findByLastModifiedAfterLastPublished(100);
if (modifiedAddresses.isEmpty()) {
LOGGER.debug("Found no modified addresss. Going to sleep.");
return true;
}
LOGGER.debug("Found {} modified addresss that will be published.", modifiedAddresses.size());
modifiedAddresses.forEach(addressProducer::sendAddress);
LOGGER.info("Found and published {} modified addresss.", keyValue("addressCount", modifiedAddresses.size()));
return false;
}
private LockConfiguration lockConfigurationForPublishModifiedAddresses() {
return new LockConfiguration("publishModifiedAddresses", Instant.now().plusSeconds(MAX_LOCK_IN_SECONDS));
}
}
@Service
public class AddressProducer {
private final AddressRepository addressRepository;
public void sendAddress(Address address) {
address.setLastPublished(Instant.now());
addressRepository.save(address);
//Do Kafka stuff
}
}
堆栈跟踪的一部分:
message: "Unexpected error occurred in scheduled task."
stack_trace: "org.springframework.orm.ObjectOptimisticLockingFailureException: Object of class [<packagename>.Address] with identifier [aa17cf6e-110e-4b1a-8866-6647b42e9155]: optimistic locking failed; nested exception is org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [<packagename>.Address#aa17cf6e-110e-4b1a-8866-6647b42e9155]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:335)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:253)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:135)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy178.save(Unknown Source)
at <packagename>.AddressProducer.sendAddress(AddressProducer.java:91)
我尝试不保存每个地址,而是收集列表然后调用saveAll()
. 另外我读到这可能是一个冲洗问题,但我无法测试它,因为我没有得到EntityManager
并且找不到手动冲洗的方法。另外我认为它应该被正确处理,如果它是事务性的。
谢谢!
问候