在使用CMT的Java EE服务器上,我使用ehcache在业务对象层(EJB)和数据访问层(使用JDBC的POJO)之间实现缓存层。我似乎遇到两个线程在使用自动填充Ehcache时访问相同记录的竞态条件。缓存被锁定在记录的主键上。Ehcache与数据库不同步
的情况是:
- 第一个线程更新记录在数据库中,并从缓存中记录(但数据库提交不一定马上发生 - 可能还有其他的查询遵循。)
- 第二个线程读取记录,导致缓存重新填充。
- 第一个线程提交事务。
这一切都发生在几分之一秒内。它导致缓存与数据库不同步,并且随后的记录读取操作将返回陈旧的缓存数据,直到执行另一个更新,或者该条目从缓存中过期。我可以在短时间内处理陈旧的数据(通常是一个事务的长度),但不是分钟,这是我想要缓存对象的时间。
对于避免这种竞赛条件的任何建议?
UPDATE:
清除后的交易已承诺肯定会是理想的高速缓存。问题是,在使用CMT的J2EE环境中,当缓存层夹在业务层(无状态会话EJB)和数据访问层之间时,如何执行此操作?
为了清楚说明这种限制,有问题的方法调用可能会或可能不会与之前或之后发生的其他方法调用处于相同的事务中。我不能强制进行提交(或者在单独的事务中执行此操作),因为这会改变事务边界与客户端代码的期望。任何后续的异常都不会回滚整个事务(在这种情况下非常规地清除缓存是可接受的副作用)。我无法控制进入交易的入口点,因为它本质上是客户可以使用的API。将清除缓存的可重复性推送到客户端应用程序是不合理的。
我希望能够延迟任何缓存清除操作,直到整个事务被EJB容器提交,但我没有办法挂钩到那个逻辑中,并用无状态会话bean运行我自己的代码。
更新#2:
最有希望的解决方案,到目前为止,短的重大设计变更的,就是用的Ehcache 2.0的JTA支持:http://ehcache.org/documentation/apis/jta
这意味着升级到了Ehcache 2。并为数据库启用XA事务,这可能会产生负面影响。但它似乎是“正确”的方式。
当然这是理想的,但如何做到这一点?我已经更新了这个问题来澄清这些限制。 – 2012-05-08 19:41:14
@Chris我已经更新了答案,希望它有帮助。 – jmruc 2012-05-09 15:11:27
嗨Kril,SessionSynchronization接口仅适用于有状态会话Bean ..如果它可用于无状态,它肯定是一个很好的解决方案。 – 2012-05-10 18:09:04