Infinispace 缓存:不一致写入并发操作

Infinispace Cache: Inconsistent writes concurrent actions

我到处搜索这个问题的解决方案,但找不到任何提示。

我的应用程序部署在 JBOSS EAP 6.4 上并使用 JDK 1.8 构建。我在独立 xml 中配置了一个本地 infinispan 缓存:

<subsystem xmlns="urn:jboss:domain:infinispan:1.5">
    <cache-container name="test-cache" default-cache="test-data-cache" jndi-name="java:jboss/infinispan/test-cache" statistics-enabled="true">
        <transport lock-timeout="60000"/>
        <local-cache name="test-data-cache" statistics-enabled="true">
            <!-- <transaction locking="PESSIMISTIC"/> -->
            <locking isolation="READ_COMMITTED"/>
            <expiration lifespan="3600000"/>
        </local-cache>
    </cache-container>
</subsystem>

我将数据放入缓存中为:

package com.comp.test;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

import org.apache.commons.collections4.MapUtils;
import org.infinispan.Cache;

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class CacheTest {

    @Resource(lookup = "java:jboss/infinispan/test-cache")
    private org.infinispan.manager.CacheContainer container;
    
    public void putData(String k, String v) {
        final Map<String, String> tmap = getDataCache().get(k);
        if (!containsData(k)) {
            final HashMap<String, String> rmap = new HashMap<>();
            rmap.put(k+"_"+v, v); 
            getDataCache().put(k, rmap);
        } else {
            getDataCache().get(k).put(k+"_"+v, v);
        }
    }
    
    public boolean containsData(String k) {
        return getDataCache().containsKey(k);
    }

    private Cache<String, Map<String, String>> getDataCache() {
        return container.getCache("test-data-cache");
    }
}

我有无状态 Bean,它可以同时将数据集合放入缓存(@Asyncronous 注释)。当所有并发操作结束后,当我从缓存中检索数据时,缓存中的值总是较少。如果我放入 20 个值,则缓存中仅存在 16 / 17 个值。

在开始将数据放入该特定密钥的缓存之前,我试图找出是否可以锁定该密钥。但我了解到它是由 Infinispan 内部处理的。我在 SO 上发现了另一个类似的问题。但这个问题也没有答案。 Infinispan cache inconsistency for massive concurrent operations

请告诉我如何确保并发放入infinispan本地缓存的数据最后是一致的。如果您需要更多信息,请告诉我。

您有两个选项可用于悲观锁定:

  1. getDataCache().getAdvancedCache().lock(k)
  2. getDataCache().getAdvancedCache().withFlags(Flag.FORCE_WRITE_LOCK)

还有第三种选择:改为使用乐观锁定,如果另一个事务修改了键,则重试事务。但这不适用于 @TransactionalAttribute,您必须自己调用 TransactionManager.commit() 并捕获 WriteSkewException.

我通过@Dan Berindei 提供的提示解决了这个问题。

我将缓存配置更改为:

<subsystem xmlns="urn:jboss:domain:infinispan:1.5">
    <cache-container name="test-cache" default-cache="test-data-cache" jndi-name="java:jboss/infinispan/test-cache" statistics-enabled="true">
        <transport lock-timeout="60000"/>
        <local-cache name="test-data-cache" start="EAGER" batching="false" statistics-enabled="true">
            <locking isolation="SERIALIZABLE" acquire-timeout="5000"/>
            <transaction mode="FULL_XA" locking="PESSIMISTIC"/>
            <expiration lifespan="3600000"/>
        </local-cache>
    </cache-container>
</subsystem>

然后我更新了我的代码以在每次调用缓存时锁定密钥:

package com.comp.test;

import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;

import org.apache.commons.collections4.MapUtils;
import org.infinispan.Cache;

@Stateless
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public class CacheTest {

    @Resource(lookup = "java:jboss/infinispan/test-cache")
    private org.infinispan.manager.CacheContainer container;
    
    public void putData(String k, String v) {
        final Map<String, String> tmap = getDataCache().get(k);
        if (!containsData(k)) {
            final HashMap<String, String> rmap = new HashMap<>();
            rmap.put(k+"_"+v, v); 
            getDataCache().put(k, rmap);
        } else {
                 AdvancedCache<String, Map<String, String>> cache = getDataCache().getAdvancedCache();
                cache.lock(k);
            getDataCache().get(k).put(k+"_"+v, v);
        }
    }
    
    public boolean containsData(String k) {
        return getDataCache().containsKey(k);
    }

    private Cache<String, Map<String, String>> getDataCache() {
        return container.getCache("test-data-cache");
    }
}