1

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并且找不到手动冲洗的方法。另外我认为它应该被正确处理,如果它是事务性的。

谢谢!

问候

4

0 回答 0