您的位置:首页 > 产品设计 > UI/UE

一次奇怪的assertOpen Issue 解决

2015-12-02 15:00 423 查看
好久没写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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: