运行 在没有查询索引的字段上进行交易期间的 Gemfire 查询

Running Gemfire Query during Transaction on field without Query Index

在 gemfire client.

中的 GF 事务期间查询区域中的字段时,我们看到以下异常
java.lang.ClassCastException: com.gemstone.gemfire.internal.cache.EntrySnapshot cannot be cast to com.gemstone.gemfire.internal.cache.LocalRegion$NonTXEntry

at com.gemstone.gemfire.internal.cache.EntriesSet$EntriesIterator.moveNext(EntriesSet.java:183) 
at com.gemstone.gemfire.internal.cache.EntriesSet$EntriesIterator.<init>(EntriesSet.java:121) 
at com.gemstone.gemfire.internal.cache.EntriesSet.iterator(EntriesSet.java:85) 
at com.gemstone.gemfire.cache.query.internal.ResultsCollectionWrapper.iterator(ResultsCollectionWrapper.java:181) 
at com.gemstone.gemfire.cache.query.internal.QRegion.iterator(QRegion.java:225) 
at com.gemstone.gemfire.cache.query.internal.CompiledSelect.doNestedIterations(CompiledSelect.java:712) 
at com.gemstone.gemfire.cache.query.internal.CompiledSelect.doIterationEvaluate(CompiledSelect.java:577) 
at com.gemstone.gemfire.cache.query.internal.CompiledSelect.evaluate(CompiledSelect.java:413) 
at com.gemstone.gemfire.cache.query.internal.DefaultQuery.executeUsingContext(DefaultQuery.java:529) 
at com.gemstone.gemfire.cache.query.internal.DefaultQuery.execute(DefaultQuery.java:365)

根据我们的反复试验,只有满足以下条件时才会发生

  1. 交易运行宁: 即调用 gemfireCache.getCacheTransactionManager().begin() 然后执行查询

  2. 不为 Query/Field 创建函数索引 : 即 QueryService.createIndex(String, String, String) 在 init

  3. 期间未在特定字段上调用

3。其中 condition 在区域数据中有一个 字段可以为 null: 即如果 运行 "SELECT * FROM /REGIONNAME WHERE fieldName = ",如果 fieldName 在某些条目中为 null,则抛出上述异常,否则,没问题。

我们正在使用从 QueryService.newQuery("SELECT * FROM /REGIONNAME WHERE fieldName = ") 获得的编译查询。如果我没记错的话,查询服务是本地服务,而不是 运行 在服务器上查询。

我们正在使用 Gemfire 8.2.1

如果您需要更多信息,请在下面发表评论。

--- 2016 年 5 月 12 日更新 ---

终于有时间整理一个简单的测试用例来说明问题:

我启动进程使用JUnit,个人习惯而已。第一个测试用例在端口 40001

上启动一个带有定位器的服务器

第二个测试用例启动客户端进程,运行在事务中进行没有索引的查询。

public class GemfireQueryInTXTest {

@Test
public void startServer() throws Exception {
    Properties props = new Properties();
    System.setProperty("gemfirePropertyFile", "query_in_tx/gfserver-query-in-tx.properties");
    String file = DistributedSystem.getPropertyFileURL().getFile();
    props.load(new FileReader(file));

    Cache cache = new CacheFactory(props).create();
    RegionFactory<String, ValueEntry> factory = cache
            .<String, ValueEntry>createRegionFactory("REPLICATE")
            .setKeyConstraint(String.class)
            .setValueConstraint(ValueEntry.class);

    Region<String, ValueEntry> valueEntryRegion = factory.create("VALUEENTRY");

    valueEntryRegion.put("first", new ValueEntry("firstEntry", "NotNull"));
    valueEntryRegion.put("second", new ValueEntry("secondEntry", null));

    CacheServer server = cache.addCacheServer();
    server.setPort(40000);

    server.start();

    Thread.sleep(1000000L);
}

@Test
public void testRunningQueryDuringTransactionOnNullableField() throws Exception {
    Properties props = new Properties();
    System.setProperty("gemfirePropertyFile", "query_in_tx/gemfire-query-in-tx.properties");
    String file = DistributedSystem.getPropertyFileURL().getFile();
    props.load(new FileReader(file));

    ClientCache cache = new ClientCacheFactory(props).create();
    ClientRegionFactory<String, ValueEntry> factory = cache
            .<String, ValueEntry>createClientRegionFactory("DEFAULT")
            .setKeyConstraint(String.class)
            .setValueConstraint(ValueEntry.class);

    Region<String, ValueEntry> valueEntryRegion = factory.create("VALUEENTRY");
    valueEntryRegion.registerInterest(".*", InterestResultPolicy.KEYS_VALUES);

    CacheTransactionManager cacheTransactionManager = cache.getCacheTransactionManager();

    QueryService localQueryService = cache.getLocalQueryService();
    Query query = localQueryService.newQuery("SELECT * from /VALUEENTRY WHERE nullable = ");
    // No Exception will be thrown if create index for the field (uncomment below);
    // localQueryService.createIndex("IndexName", "nullable", "/VALUEENTRY");

    // ... Or run without transaction (comment below tx opening and closing)
    cacheTransactionManager.begin();
    System.out.println("Before Query Executed");
    query.execute(new Object[]{"1"});
    System.out.println("After Query Executed");
    cacheTransactionManager.commit();
}
}

域对象:ValueEntry.java

public class ValueEntry implements DataSerializable {
private String notNull;
private String nullable;

public ValueEntry() {
}

public ValueEntry(String notNull, String nullable) {
    this.notNull = notNull;
    this.nullable = nullable;
}

public String getNotNull() {
    return notNull;
}

public String getNullable() {
    return nullable;
}

@Override
public void toData(DataOutput dataOutput) throws IOException {
    DataSerializer.writeString(notNull, dataOutput);
    DataSerializer.writeString(nullable, dataOutput);
}

@Override
public void fromData(DataInput dataInput) throws IOException, ClassNotFoundException {
    this.notNull = DataSerializer.readString(dataInput);
    this.nullable = DataSerializer.readString(dataInput);
}
}

服务器属性和xml:

cache-xml-file=query_in_tx\cache-server.xml
start-locator=40001
locators=localhost[40001]
log-file=logs\server.log
log-level=config
mcast-port=0
name=server

<cache>
<serialization-registration>
    <instantiator id="999">
        <class-name>com.testing.gemfire.domain.ValueEntry</class-name>
    </instantiator>
</serialization-registration>
</cache>

客户端属性和 xml:

cache-xml-file=query_in_tx\cache-query-in-tx.xml
log-disk-space-limit=100
log-file-size-limit=20
log-file=logs\cache.log
log-level=config
mcast-port=0
name=gemfire-playground

<client-cache>
    <pool name="Zero" subscription-enabled="true" read-timeout="3000"
          retry-attempts="5" socket-buffer-size="65536">
        <locator host="localhost" port="40001" />
    </pool>

    <region-attributes id="DEFAULT" refid="CACHING_PROXY" pool-name="Zero"/>
</client-cache>

经与Pivotal团队沟通,发现这是Gemfire客户端的一个bug。如问题描述中所述,创建索引将阻止抛出此异常。

以后版本修复的话我会再更新