0

我们基于 ATG 构建的电子商务应用程序具有多个用户可以更新同一订单的规定。由于 Order 的缓存模式是Simple- 这导致了大量的ConcurrentUpdateExceptionand InvalidVersionException。我们正在考虑locked缓存模式,但是对使用锁定缓存持怀疑态度,因为订单更新非常频繁,锁定可能会导致死锁并有其自身的性能影响。

有没有办法可以继续使用简单缓存模式并尽量减少 ConcurrentUpdateException 和 InvalidVersionException 的发生?

4

2 回答 2

3

我的经验是,您必须在任何中到高容量 ATG 网站上对订单使用锁定缓存。另外,请记住,当这种情况发生时,最终用户体验很差,因为他们要么收到错误消息(如果错误处理是好)或者他们得到类似“内部服务器错误”的错误。

我认为您需要对订单使用锁定缓存的原因是:

  1. 您不能保证用户没有同时打开多个更新购物车的会话(这只是一个不完整的订单)。我还看到客户与家人分享他们的登录信息等的例子,然后想知道为什么所有这些物品总是神奇地出现在他们的购物车中。
  2. 有许多流程可以更新订单,包括使用 CSC 模块的场景和客户服务代理等内容。
  3. 您可能拥有以非安全方式更新订单的代码。

一些可能有帮助的事情包括:

  • 始终使用OrderManager加载/更新订单。听起来很明显,但我通过存储库看到了很多更新订单。
  • 确保任何更新都在事务块内。
  • 尝试合并任何可能更新订单的后台进程以在您的 ATG 实例的一小部分上运行(这将有助于减少并发性)

ATG帮助对此有这样的说法:

多服务器应用程序可能需要锁定缓存,其中一次只有一个 Oracle ATG Web Commerce 实例对给定项目类型的缓存数据具有写访问权限。您可以使用锁定缓存来防止多个服务器尝试同时更新同一个项目 - 例如,Commerce 订单项目可以由面向外部的服务器上的客户和面向内部的服务器上的客户服务代理更新。通过限制写访问,锁定缓存可确保所有 Oracle ATG Web Commerce 实例之间的缓存数据视图保持一致。

也就是说,转换为锁定缓存肯定需要对订单存储库缓存进行性能测试和调整。它可以而且确实会导致死锁(见过很多次),但如果配置正确,死锁很少发生。

不确定您使用的是什么版本的 ATG,但对于 10.2,这里有一个很好的解释说明如何让所有内容“同步”。

于 2014-08-28T23:22:38.657 回答
1

实际上,很久以前在 Legacy ATG 社区中推荐了一种最佳实践方法。只是贴在这里。

当您将 Order 对象与同步和事务一起使用时,需要遵循一个特定的使用模式。不遵循预期的模式会导致不必要的 ConcurrentUpdateExceptions、InvalidVersionExceptions 和死锁。在您的代码中必须严格遵守以下顺序:

  1. 获取配置文件 ID 的本地锁定。
  2. 开始交易
  3. 按订单同步
  4. 对订单对象执行所有修改。
  5. 调用 OrderManager.updateOrder。
  6. 结束同步
  7. 结束交易。
  8. 释放配置文件 ID 上的本地锁定。

第 1、2、7、8 步在需要更新订单的 ATG 表单处理程序的 beforeSet() 和 afterSet() 方法中为您完成。其中包括扩展 PurchaseProcessFormHandler 和 OrderModifierFormHandler(已弃用)的表单处理程序。如果您的代码在 PurchaseProcessFormHandler 之外访问/修改订单,则可能需要手动获取本地锁。可以使用 TransactionLockService 来获取锁。

因此,如果您扩展了基于 PurchaseProcessFormHandler 的 ATG 表单处理程序,并在更新订单的 handleXXX() 方法中编写了自定义代码,您的代码应如下所示:

synchronized( order )
{
  // Do order updates
  orderManager.updateOrder( order );
}

如果您编写了自定义代码来更新 PurchaseProcessFormHandler 之外的订单(例如 CouponFormHandler、droplet、pipeline servlet、fulfillment-related),您的代码应该如下所示:

ClientLockManager lockManager = getLocalLockManager(); // Should be configured as /atg/commerce/order/LocalLockManager
boolean acquireLock = false;
try
{
  acquireLock = !lockManager.hasWriteLock( profileId, Thread.currentThread() );
  if ( acquireLock )
    lockManager.acquireWriteLock( profileId, Thread.currentThread() );

  TransactionDemarcation td = new TransactionDemarcation();
  td.begin( transactionManager );
  boolean shouldRollback = false;
  try
  {
    synchronized( order )
    {
      // do order updates
      orderManager.updateOrder( order );
    }
  }
  catch ( ... e )
  {
    shouldRollback = true;
    throw e;
  }
  finally
  {
    try
    {
      td.end( shouldRollback );
    }
    catch ( Throwable th )
    {
      logError( th );
    }
  }
}
finally
{
  try
  {
    if ( acquireLock )
      lockManager.releaseWriteLock( profileId, Thread.currentThread(), true );
  }
  catch( Throwable th )
  {
    logError( th );
  }
}

当多个线程尝试更新同一个ATG 实例上的相同顺序时,此模式仅对防止 ConcurrentUpdateExceptions、InvalidVersionExceptions 和死锁有用。这对于商业站点上的大多数情况应该是足够的,因为会话粘性会将更新限制在同一 ATG 实例的同一订单上。

于 2015-08-07T21:51:00.807 回答