【问题标题】:Concurrency issue with jta-jpa-pessmistic-writejta-jpa-pessmistic-write 的并发问题
【发布时间】:2018-04-09 02:40:03
【问题描述】:

我正在使用带有 JPA2.1(EclipseLink) 的 Glassfish v4 和 EJB 应用程序。我遇到了 JTA 的并发问题。基本上,我正在尝试通过给定名称查找实体,然后使用新的 nextVal 更新列。 所以一个 EJB 无状态 Bean

@PersistenceContext(unitName = "db")
private EntityManager em;

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public List<Long> getWithWriteLock(final String name, final int size) {
    TypedQuery<Sequence> q = em.createNamedQuery("Example.findByName", Example.class);
    q.setParameter("name", name);
    q.setLockMode(LockModeType.PESSIMISTIC_WRITE);
    Example example = q.getSingleResult();
    final Long result = example.getNextval().longValue();
    final Long nextVal = result + size;
    final List<Long> resultList = LongStream.range(result, nextVal).boxed().collect(Collectors.toList());
    example.setNextval(BigInteger.valueOf(nextVal));
    em.merge(example);
    em.flush(); //forced flush
    return resultList;
}

应用程序部署在多个节点上,此方法通过带有 WRITE 锁的 EJB Singleton 进行评估。我们看到的是返回的列表是重复的随机时间。当不同的线程调用此方法时甚至超过一分钟时,它甚至会发生。我们尝试过显式刷新、合并甚至验证确实使用 SELECT ... FOR UPDATE 语句的 SQL 日志。 如果有人看到过这样的事情,请告诉我。

谢谢。

【问题讨论】:

  • “flush()”到底在做什么?不应该是 em.flush() 吗?另外,当您稍后使用 em.merge 时,为什么要使用 hw2EntityManager 实例来创建查询 - 它们应该是相同的 EntityManager 或者您的读取发生在不同的上下文中。
  • 是的,它的 em.flush()。我更新了我的示例 sn-p。 em 上下文也一样。
  • 您必须检查日志,因为如果一个事务回滚,可能会发生这种情况。 JPA 日志记录应该显示您更新对象所使用的值,因此允许您跟踪哪些其他线程放入了这些相同的值,并给出处理重复的原因的线索。
  • 谢谢@Chris。所以我们已经确认没有异常,并且这个方法在新事务中,所以即使调用者事务被回滚,这仍然会通过。 SQL 日志说我们做了一个正确的 SELCT For update,然后只正确地将 nextVal 更新为 'X' 一次。但是问题似乎是 2 个线程同时调用相同的方法,并且它们都同时获取数据的副本,最终 List 结果是相同的。我们从带有 WRITE 锁的 Singleton 调用此方法。有什么想法吗?
  • 悲观锁依赖于数据库工作,看起来你的允许第二个查询返回而不是等待。

标签: hibernate concurrency ejb eclipselink jta


【解决方案1】:

我认为您遇到了 EclipseLink 的缓存。

您应该禁用此实体的缓存:

@Cacheable(false)
@Entity
public class Example {
     ...
}

另见EclipseLink/FAQ/How to disable the shared cache?

【讨论】:

  • 这不是因为我们已经有了,我们还尝试了完全相同的方法,将命名查询转换为本地查询以排除任何 JPA 缓存,并修改命名查询以添加 EclipseLink 提示以绕过缓存,刷新等
【解决方案2】:

EJB Singleton 只能在单个 jvm 中工作,在多个节点中 环境中,您必须依靠数据库的锁。 不要使用REQUIRES_NEW,仅在必要时使用。如果有外部事务,REQUIRES_NEW 事务中发生的回滚不会影响外部事务。因为它们是不同的交易。应用程序停止外部事务并开始并提交或回滚内部事务,然后恢复外部事务。

【讨论】:

  • 是的,我知道。然而,Singleton 并不适合悲观锁。但是,为此我们必须依赖 DB,DB 正在发出 XA 回滚,只是 EJB 没有捕捉到它。所以我们的应用无法知道更新从未发生过,这会导致我们的应用返回重复列表。
猜你喜欢
  • 1970-01-01
  • 2015-12-28
  • 1970-01-01
  • 1970-01-01
  • 2010-12-23
  • 1970-01-01
  • 2011-05-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多