一次奇怪的assertOpen Issue 解决

52 篇文章 0 订阅

好久没写Blog,最近事多,人忙。现在有空闲下来说说自己在最近一次jpa问题解决时候的实例。
给出出问题的代码的简单实现如下:

CacheService
@Autowired
private DistributedMap<ID,T> cache;
@Autowired
private PersistenceService service;
public void get(ID id){
    T t = cache.get(id);
    if(t != null){
       return t;
    } else {
       t = service.loadForCache(ID);
       cache.put(id,t);
       return t;
    }   
}
Persistent Layer
public class PersistenceService {
  public T loadForCache(ID id){
     Query query = createQuery("select t from T where t.id=?");
     query.setFetchPlan("detail");
     ...
  }
  ...
}
@Entity
@FetchGroups({
    @FetchGroup(name="detail", attributes={
        @FetchAttribute(name="refs")
    }),
    ...
})
public class T {
  private ID id;
  private Map<ID, A> refs = new HashMap<ID,A>();
  ...
  ...

}
Scheduled task:
@Autowired
private CacheService cache;
@Transactional
public void dailyProcess(){
  T t = cache.get(id);
  ......
}

系统使用了IBM Websphere cluster distributed map做为集群的缓存,get函数简单的先从cache拿,拿不到再通过jpa从数据库load数据,由于数据比较复杂,使用了fetchplan来获取需要序列化的部分属性,由于使用了fetchplan,所以不再需要使用transaction来load数据了。
最近我为系统添加了一个新的scheduled task, 每分钟执行一次,在测试中发现每天总能出现2,3次openjpa assertOPEN issue,而且每个节点都会出。每次报错都是在cache.put(id,t)这一行,试图序列化T对象出错,报错的trace stack可能是任何一个操作websphere cache的地方(系统中使用了多个websphere的cache)。打开openjpa的runtime trace level日志,可以看出系统每次实际出错都是在我新加的scheduled的任务执行时,之所以看起来每次错误日志地方不一样是因为cache.put操作的实现可能是在另外一个线程中执行(Websphere daemon batch operation thread)。。。所以任何一个操作websphere cache的get/set都有可能导致报错。
现在说说具体报错原因,之前get方法不在事物中,openjpa实现会在get方法调用loadForCache的时候按照fetchplan要求取出所有需要的数据,而我新加的方法讲get放进事物里,那么问题就出现了,由于openjpa的优化,fetchplan的获取可能不在是即时的(t = service.loadForCache(ID);),而cache.put(id,t)操作的实现可能要在websphere的后台线程里面做处理。。。很明显后台线程处理的时候很可能数据还没有load完,assertOpen异常就出现了。

这个教训告诉我,一定要多去了解你要是用的第三方代码,在事务处理的时候千万要小心不要有代码是提交事务相关信息去其他线程去处理的。。。

要解决这个问题有2个办法,第一个,在loadForCache方法上加上@transactional(propagation=Propagation.REQUIRES_NEW),还有一个就是手动编写transaction相关代码,只把需要的代码放入事务

@Autowired
private PlatformTransactionManager txManager;
public void dailyProcess(){
  T t = cache.get(id);
  TransactionDefinition def = new DefaultTransactionDefinition();
  //def.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRED); 
//def.setIsolationLevel(DefaultTransactionDefinition.ISOLATION_READ_COMMITTED); 
  TransactionStatus txStatus = txManager.getTransaction(def);
  boolean result = false;
  try {
  result = service.process(t);
  txManager.commit(txStatus);
  } catch (Exception e) {
result = false;
txManager.rollback(txStatus);
LOGGER.error("Transfer Error!",e);
}
return result;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值