我们的电子商务应用程序建立在ATG之上,可以让多个用户更新同一个订单。由于订单的缓存模式为Simple
,因此产生了大量ConcurrentUpdateException
和InvalidVersionException
。我们正在考虑locked
缓存模式,但是对于使用锁定缓存持怀疑态度,因为Orders正在非常频繁地更新,并且锁定可能导致死锁并且具有其自身的性能影响。
有没有办法可以继续使用简单缓存模式并最大限度地减少ConcurrentUpdateException和InvalidVersionException的出现?
答案 0 :(得分:3)
我的经验是你必须在任何中等到高容量的ATG网站上使用锁定缓存。此外,请记住,当发生这种情况时最终用户体验很糟糕,因为他们要么收到错误消息(如果错误处理是好的)或者他们得到类似于"内部服务器错误"错误。
我认为您需要对订单使用锁定缓存的原因是:
可能有帮助的一些事情包括:
OrderManager
加载/更新订单。听起来很明显但我通过存储库看到了很多更新订单。ATG help对此有所说明:
多服务器应用程序可能需要锁定缓存,其中一次只有一个Oracle ATG Web Commerce实例具有对给定项类型的缓存数据的写访问权。您可以使用锁定缓存来防止多个服务器同时尝试更新同一项目 - 例如,Commerce订单项,可以由面向外部的服务器上的客户和面向内部的服务器上的客户服务代理更新。通过限制写访问,锁定缓存可确保在所有Oracle ATG Web Commerce实例之间保持一致的缓存数据视图。
那说转换为锁定缓存肯定需要性能测试和订单存储库缓存的调优。它可以并确实导致死锁(多次看到)但如果配置正确,死锁很少发生。
不确定您正在使用的是什么版本的ATG,但对于10.2,您可以很好地解释here如何获得所有内容"同步"。
答案 1 :(得分:1)
很久以前,Legacy ATG社区推荐了一种最佳实践方法。只是粘贴在这里。
当您将Order对象与同步和事务一起使用时,有一个特定的使用模式是至关重要的。不遵循预期的模式可能导致不必要的ConcurrentUpdateExceptions,InvalidVersionExceptions和死锁。您的代码中必须严格遵守以下顺序:
在ATG表单处理程序的beforeSet()和afterSet()方法中为您完成了步骤1,2,7,8,其中需要更新订单。这些包括扩展PurchaseProcessFormHandler和OrderModifierFormHandler(不建议使用)的表单处理程序。如果您的代码访问/修改PurchaseProcessFormHandler之外的订单,则可能需要手动获取本地锁。可以使用TransactionLockService完成锁定获取。
因此,如果您已经基于PurchaseProcessFormHandler扩展了一个ATG表单处理程序,并且在一个更新订单的handleXXX()方法中编写了自定义代码,那么您的代码应如下所示:
synchronized( order )
{
// Do order updates
orderManager.updateOrder( order );
}
如果您已编写自定义代码更新PurchaseProcessFormHandler之外的订单(例如CouponFormHandler,Droplet,管道Servlet,履行相关),您的代码应如下所示:
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实例。