/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ignite.internal.processors.cache.distributed.replicated;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
import java.util.Iterator;
import java.util.UUID;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import javax.cache.Cache;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteException;
import org.apache.ignite.cache.CacheMode;
import org.apache.ignite.cache.query.QueryCursor;
import org.apache.ignite.cache.query.SqlQuery;
import org.apache.ignite.cache.query.annotations.QuerySqlField;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.events.DiscoveryEvent;
import org.apache.ignite.events.Event;
import org.apache.ignite.events.EventType;
import org.apache.ignite.internal.processors.cache.IgniteCacheAbstractQuerySelfTest;
import org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing;
import org.apache.ignite.internal.util.typedef.X;
import org.apache.ignite.internal.util.typedef.internal.S;
import org.apache.ignite.internal.util.typedef.internal.U;
import org.apache.ignite.lang.IgnitePredicate;
import org.apache.ignite.testframework.GridTestUtils;
import org.apache.ignite.testsuites.IgniteIgnore;
import org.apache.ignite.transactions.Transaction;
import static org.apache.ignite.cache.CacheMode.REPLICATED;
import static org.apache.ignite.cache.CachePeekMode.ALL;
import static org.apache.ignite.events.EventType.EVT_NODE_LEFT;
/**
* Tests replicated query.
*/
public class IgniteCacheReplicatedQuerySelfTest extends IgniteCacheAbstractQuerySelfTest {
/** */
private static final boolean TEST_DEBUG = false;
/** Grid1. */
private static Ignite ignite1;
/** Grid2. */
private static Ignite ignite2;
/** Grid3. */
private static Ignite ignite3;
/** Cache1. */
private static IgniteCache<CacheKey, CacheValue> cache1;
/** Cache2. */
private static IgniteCache<CacheKey, CacheValue> cache2;
/** Cache3. */
private static IgniteCache<CacheKey, CacheValue> cache3;
/** Key serialization cnt. */
private static volatile int keySerCnt;
/** Key deserialization count. */
private static volatile int keyDesCnt;
/** Value serialization count. */
private static volatile int valSerCnt;
/** Value deserialization count. */
private static volatile int valDesCnt;
/** {@inheritDoc} */
@Override protected int gridCount() {
return 3;
}
/** {@inheritDoc} */
@Override protected CacheMode cacheMode() {
return REPLICATED;
}
/** {@inheritDoc} */
@Override protected void beforeTestsStarted() throws Exception {
super.beforeTestsStarted();
ignite1 = grid(0);
ignite2 = grid(1);
ignite3 = grid(2);
cache1 = jcache(ignite1, CacheKey.class, CacheValue.class);
cache2 = jcache(ignite2, CacheKey.class, CacheValue.class);
cache3 = jcache(ignite3, CacheKey.class, CacheValue.class);
}
/** {@inheritDoc} */
@Override protected void afterTestsStopped() throws Exception {
super.afterTestsStopped();
ignite1 = null;
ignite2 = null;
ignite3 = null;
cache1 = null;
cache2 = null;
cache3 = null;
}
/**
* @throws Exception If failed.
*/
public void testClientOnlyNode() throws Exception {
try {
Ignite g = startGrid("client");
IgniteCache<Integer, Integer> c = jcache(g, Integer.class, Integer.class);
for (int i = 0; i < 10; i++)
c.put(i, i);
// Client cache should be empty.
assertEquals(0, c.localSize());
Collection<Cache.Entry<Integer, Integer>> res =
c.query(new SqlQuery<Integer, Integer>(Integer.class, "_key >= 5 order by _key")).getAll();
assertEquals(5, res.size());
int i = 5;
for (Cache.Entry<Integer, Integer> e : res) {
assertEquals(i, e.getKey().intValue());
assertEquals(i, e.getValue().intValue());
i++;
}
}
finally {
stopGrid("client");
}
}
/**
* JUnit.
*
* @throws Exception If failed.
*/
public void testIterator() throws Exception {
int keyCnt = 100;
for (int i = 0; i < keyCnt; i++)
cache1.put(new CacheKey(i), new CacheValue("val" + i));
assertEquals(keyCnt, cache1.localSize(ALL));
assertEquals(keyCnt, cache2.localSize(ALL));
assertEquals(keyCnt, cache3.localSize(ALL));
QueryCursor<Cache.Entry<CacheKey, CacheValue>> qry =
cache1.query(new SqlQuery<CacheKey, CacheValue>(CacheValue.class, "true"));
Iterator<Cache.Entry<CacheKey, CacheValue>> iter = qry.iterator();
assert iter.hasNext();
int cnt = 0;
while (iter.hasNext()) {
iter.next();
cnt++;
}
assertEquals(keyCnt, cnt);
}
/**
* @throws Exception If test failed.
*/
public void testLocalQuery() throws Exception {
cache1.clear();
Transaction tx = ignite1.transactions().txStart();
try {
cache1.put(new CacheKey(1), new CacheValue("1"));
cache1.put(new CacheKey(2), new CacheValue("2"));
cache1.put(new CacheKey(3), new CacheValue("3"));
cache1.put(new CacheKey(4), new CacheValue("4"));
tx.commit();
info("Committed transaction: " + tx);
}
catch (IgniteException e) {
tx.rollback();
throw e;
}
checkQueryResults(cache1);
checkQueryResults(cache2);
checkQueryResults(cache3);
}
/**
* @throws Exception If test failed.
*/
public void testDistributedQuery() throws Exception {
int keyCnt = 4;
final CountDownLatch latch = new CountDownLatch(keyCnt * 2);
IgnitePredicate<Event> lsnr = new IgnitePredicate<Event>() {
@Override public boolean apply(Event evt) {
latch.countDown();
return true;
}
};
ignite2.events().localListen(lsnr, EventType.EVT_CACHE_OBJECT_PUT);
ignite3.events().localListen(lsnr, EventType.EVT_CACHE_OBJECT_PUT);
Transaction tx = ignite1.transactions().txStart();
try {
for (int i = 1; i <= keyCnt; i++)
cache1.put(new CacheKey(i), new CacheValue(String.valueOf(i)));
tx.commit();
info("Committed transaction: " + tx);
}
catch (IgniteException e) {
tx.rollback();
throw e;
}
latch.await();
QueryCursor<Cache.Entry<CacheKey, CacheValue>> qry =
cache1.query(new SqlQuery<CacheKey, CacheValue>(CacheValue.class, "val > 1 and val < 4"));
// Distributed query.
assertEquals(2, qry.getAll().size());
// Create new query, old query cannot be modified after it has been executed.
qry = cache3.query(new SqlQuery<CacheKey, CacheValue>(CacheValue.class, "val > 1 and val < 4").setLocal(true));
// Tests execute on node.
Iterator<Cache.Entry<CacheKey, CacheValue>> iter = qry.iterator();
assert iter != null;
assert iter.hasNext();
iter.next();
assert iter.hasNext();
iter.next();
assert !iter.hasNext();
}
/**
* @throws Exception If test failed.
*/
public void testToString() throws Exception {
int keyCnt = 4;
for (int i = 1; i <= keyCnt; i++)
cache1.put(new CacheKey(i), new CacheValue(String.valueOf(i)));
// Create query with key filter.
QueryCursor<Cache.Entry<CacheKey, CacheValue>> qry =
cache1.query(new SqlQuery<CacheKey, CacheValue>(CacheValue.class, "val > 0"));
assertEquals(keyCnt, qry.getAll().size());
}
/**
* @throws Exception If failed.
*/
public void testLostIterator() throws Exception {
IgniteCache<Integer, Integer> cache = jcache(Integer.class, Integer.class);
for (int i = 0; i < 1000; i++)
cache.put(i, i);
QueryCursor<Cache.Entry<Integer, Integer>> fut = null;
for (int i = 0; i < cache.getConfiguration(CacheConfiguration.class).getMaxQueryIteratorsCount() + 1; i++) {
QueryCursor<Cache.Entry<Integer, Integer>> q =
cache.query(new SqlQuery<Integer, Integer>(Integer.class, "_key >= 0 order by _key"));
assertEquals(0, (int)q.iterator().next().getKey());
if (fut == null)
fut = q;
}
final QueryCursor<Cache.Entry<Integer, Integer>> fut0 = fut;
GridTestUtils.assertThrows(log, new Callable<Object>() {
@Override public Object call() throws Exception {
int i = 0;
Cache.Entry<Integer, Integer> e;
while ((e = fut0.iterator().next()) != null)
assertEquals(++i, (int)e.getKey());
return null;
}
}, IgniteException.class, null);
}
/**
* @throws Exception If failed.
*/
@IgniteIgnore(value = "https://issues.apache.org/jira/browse/IGNITE-613", forceFailure = true)
public void testNodeLeft() throws Exception {
Ignite g = startGrid("client");
try {
assertTrue(g.configuration().isClientMode());
IgniteCache<Integer, Integer> cache = jcache(Integer.class, Integer.class);
for (int i = 0; i < 1000; i++)
cache.put(i, i);
// Client cache should be empty.
assertEquals(0, cache.localSize());
QueryCursor<Cache.Entry<Integer, Integer>> q =
cache.query(new SqlQuery<Integer, Integer>(Integer.class, "_key >= 0 order by _key").setPageSize(10));
assertEquals(0, (int)q.iterator().next().getKey());
// Query for replicated cache was run on one of nodes.
ConcurrentMap<?, ?> mapNode1 = queryResultMap(0);
ConcurrentMap<?, ?> mapNode2 = queryResultMap(1);
ConcurrentMap<?, ?> mapNode3 = queryResultMap(2);
assertEquals(1, mapNode1.size() + mapNode2.size() + mapNode3.size());
final UUID nodeId = g.cluster().localNode().id();
final CountDownLatch latch = new CountDownLatch(1);
grid(0).events().localListen(new IgnitePredicate<Event>() {
@Override public boolean apply(Event evt) {
if (((DiscoveryEvent)evt).eventNode().id().equals(nodeId))
latch.countDown();
return true;
}
}, EVT_NODE_LEFT);
stopGrid("client");
latch.await();
assertEquals(0, mapNode1.size());
assertEquals(0, mapNode2.size());
assertEquals(0, mapNode3.size());
}
finally {
stopGrid("client");
}
}
/**
* @param node Node index.
* @return Query results map.
*/
private ConcurrentMap<?, ?> queryResultMap(int node) {
return U.field(((IgniteH2Indexing)U.field(grid(node).context().query(), "idx")).mapQueryExecutor(), "qryRess");
}
/**
* @param cache Cache.
* @throws Exception If check failed.
*/
private void checkQueryResults(IgniteCache<CacheKey, CacheValue> cache) throws Exception {
QueryCursor<Cache.Entry<CacheKey, CacheValue>> qry =
cache.query(new SqlQuery<CacheKey, CacheValue>(CacheValue.class, "val > 1 and val < 4").setLocal(true));
Iterator<Cache.Entry<CacheKey, CacheValue>> iter = qry.iterator();
assert iter != null;
assert iter.hasNext();
Cache.Entry<CacheKey, CacheValue> entry = iter.next();
assert entry.getKey().equals(new CacheKey(2)) || entry.getKey().equals(new CacheKey(3));
assert iter.hasNext();
entry = iter.next();
assert entry.getKey().equals(new CacheKey(2)) || entry.getKey().equals(new CacheKey(3));
assert !iter.hasNext();
}
/**
* Cache key.
*/
public static class CacheKey implements Externalizable {
/** Key. */
private int key;
/**
* @param key Key.
*/
CacheKey(int key) {
this.key = key;
}
/**
*
*/
public CacheKey() {
/* No-op. */
}
/**
* @return Key.
*/
public int getKey() {
return key;
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
key = in.readInt();
keyDesCnt++;
if (TEST_DEBUG)
X.println("Deserialized demo key [keyDesCnt=" + keyDesCnt + ", key=" + this + ']');
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(key);
keySerCnt++;
if (TEST_DEBUG)
X.println("Serialized demo key [serCnt=" + keySerCnt + ", key=" + this + ']');
}
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
CacheKey cacheKey;
if (o instanceof CacheKey)
cacheKey = (CacheKey)o;
else
return false;
return key == cacheKey.key;
}
/** {@inheritDoc} */
@Override public int hashCode() {
return key;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(CacheKey.class, this);
}
}
/**
* Cache value..
*/
public static class CacheValue implements Externalizable {
/** Value. */
@QuerySqlField
private String val;
/**
* @param val Value.
*/
CacheValue(String val) {
this.val = val;
}
/**
*
*/
public CacheValue() {
/* No-op. */
}
/**
* @return Value.
*/
public String getValue() {
return val;
}
/** {@inheritDoc} */
@Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
val = U.readString(in);
valDesCnt++;
if (TEST_DEBUG)
X.println("Deserialized demo value [valDesCnt=" + valDesCnt + ", val=" + this + ']');
}
/** {@inheritDoc} */
@Override public void writeExternal(ObjectOutput out) throws IOException {
U.writeString(out, val);
valSerCnt++;
if (TEST_DEBUG)
X.println("Serialized demo value [serCnt=" + valSerCnt + ", val=" + this + ']');
}
/** {@inheritDoc} */
@Override public boolean equals(Object o) {
if (this == o)
return true;
if (o == null || getClass() != o.getClass())
return false;
CacheValue val = (CacheValue)o;
return !(this.val != null ? !this.val.equals(val.val) : val.val != null);
}
/** {@inheritDoc} */
@Override public int hashCode() {
return val != null ? val.hashCode() : 0;
}
/** {@inheritDoc} */
@Override public String toString() {
return S.toString(CacheValue.class, this);
}
}
}