Hi Evgenii,
Thanks for the answer.
Unfortunately it seems it does matter. If I do a query inside a transaction
and use an instance of a cache that differs of a cache inside the query, I
will get the following exception (note I do it on client node):
Exception in thread "main" javax.cache.CacheException: Cannot start/stop
cache within lock or transaction.
at
org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl.query(IgniteCacheProxyImpl.java:676)
at
org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl.query(IgniteCacheProxyImpl.java:615)
at
org.apache.ignite.internal.processors.cache.GatewayProtectedCacheProxy.query(GatewayProtectedCacheProxy.java:356)
at simax.test.Startup.main(Startup.java:39)
Caused by: class org.apache.ignite.IgniteException: Cannot start/stop cache
within lock or transaction.
at
org.apache.ignite.internal.processors.cache.GridCacheProcessor.checkEmptyTransactions(GridCacheProcessor.java:4251)
at
org.apache.ignite.internal.processors.cache.GridCacheProcessor.dynamicStartCache(GridCacheProcessor.java:2942)
at
org.apache.ignite.internal.processors.cache.GridCacheProcessor.dynamicStartCache(GridCacheProcessor.java:2884)
at
org.apache.ignite.internal.processors.cache.GridCacheProcessor.createMissingQueryCaches(GridCacheProcessor.java:4114)
at
org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.prepareStatementAndCaches(IgniteH2Indexing.java:2017)
at
org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.parseAndSplit(IgniteH2Indexing.java:1796)
at
org.apache.ignite.internal.processors.query.h2.IgniteH2Indexing.querySqlFields(IgniteH2Indexing.java:1652)
at
org.apache.ignite.internal.processors.query.GridQueryProcessor$4.applyx(GridQueryProcessor.java:2035)
at
org.apache.ignite.internal.processors.query.GridQueryProcessor$4.applyx(GridQueryProcessor.java:2030)
at
org.apache.ignite.internal.util.lang.IgniteOutClosureX.apply(IgniteOutClosureX.java:36)
at
org.apache.ignite.internal.processors.query.GridQueryProcessor.executeQuery(GridQueryProcessor.java:2578)
at
org.apache.ignite.internal.processors.query.GridQueryProcessor.querySqlFields(GridQueryProcessor.java:2044)
at
org.apache.ignite.internal.processors.cache.IgniteCacheProxyImpl.query(IgniteCacheProxyImpl.java:664)
It seems the issue is in starting cache used in the query. If I get
IgniteCache instance for that cache, it will be started before the
transaction. But if it's never used in Ignite.cache(..) or
ignite.getOrCreateCache(..), the cache will be started inside query(..)
method which is executed in the transaction and I will get the above listed
exception. As workaround, I need to pre-initialize all caches used in
queries using either Ignite.cache(..) or ignite.getOrCreateCache(..). Note
that all caches are configured in XML configuration on server nodes only
but the queries is executed on a client node.
The code is very simple:
public class Startup {
private static final Logger log = LogManager.getLogger(Startup.class);
public static void main(String[] args) {
boolean client = args.length >= 1 &&
"client".equalsIgnoreCase(args[0]);
Ignite ignite;
if (client) {
log.info("Starting client node...");
ignite = Ignition.start("client.xml");
} else {
log.info("Starting server node...");
ignite = Ignition.start("server.xml");
}
log.info("Cache names: " + ignite.cacheNames());
IgniteCache<?, ?> cache1 = ignite.cache("Cache1");
try (Transaction tx = ignite.transactions().txStart()) {
// query from cache1 using instance of cache1
List<List<?>> r1 = cache1.query(new SqlFieldsQuery("SELECT *
FROM Cache1")).getAll(); // No exception here because Cache1 is already
initialized by ignite.cache("Cache1")
log.info("Result from cache1: " + r1);
}
try (Transaction tx = ignite.transactions().txStart()) {
// query from cache2 using instance of cache1
List<List<?>> r2 = cache1.query(new SqlFieldsQuery("SELECT *
FROM Cache2")).getAll(); // <<< Got exception here if invoked on client
node (clientMode=true)
log.info("Result from cache2: " + r2);
}
}
}
client.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="clientMode" value="true"/>
</bean>
</beans>
server.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
<bean class="org.apache.ignite.configuration.IgniteConfiguration">
<property name="clientMode" value="false"/>
<property name="cacheConfiguration">
<list>
<bean
class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="Cache1"/>
<property name="sqlSchema" value="PUBLIC"/>
<property name="queryEntities">
<list>
<bean
class="org.apache.ignite.cache.QueryEntity">
<property name="keyType" value="Cache1Key"/>
<property name="keyFields">
<set>
<value>id1</value>
<value>id2</value>
</set>
</property>
<property name="valueType" value="Cache1"/>
<property name="fields">
<map>
<entry key="id1"
value="java.lang.String"/>
<entry key="id2"
value="java.lang.String"/>
<entry key="aaa"
value="java.lang.String"/>
<entry key="bbb"
value="java.lang.String"/>
<entry key="ccc"
value="java.lang.String"/>
</map>
</property>
</bean>
</list>
</property>
</bean>
<bean
class="org.apache.ignite.configuration.CacheConfiguration">
<property name="name" value="Cache2"/>
<property name="sqlSchema" value="PUBLIC"/>
<property name="queryEntities">
<list>
<bean
class="org.apache.ignite.cache.QueryEntity">
<property name="keyType" value="Cache2Key"/>
<property name="keyFields">
<set>
<value>id3</value>
<value>id4</value>
</set>
</property>
<property name="valueType" value="Cache2"/>
<property name="fields">
<map>
<entry key="id3"
value="java.lang.String"/>
<entry key="id4"
value="java.lang.String"/>
<entry key="xxx"
value="java.lang.String"/>
<entry key="yyy"
value="java.lang.String"/>
<entry key="zzz"
value="java.lang.String"/>
</map>
</property>
</bean>
</list>
</property>
</bean>
</list>
</property>
</bean>
</beans>
Maybe I do something incorrectly?
Thanks,
Maxim
пн, 23 июл. 2018 г. в 17:51, Evgenii Zhuravlev <[email protected]>:
> Hi Maxim,
>
> It doesn't matter, which cache you will use for SQL queries here, so, yes,
> you can use some dummy cache. In future, afaik, there are plans to change
> this API, the method will be placed in Ignite class instead of IgniteCache.
>
> Regards,
> Evgenii
>
> 2018-07-23 17:41 GMT+03:00 Maxim Malygin <[email protected]>:
>
>> Hi,
>>
>> I have the question regarding Java API for SQL queries.
>> As I understand from the documentation, API for SQL queries is placed in
>> IgniteCache interface ("query" method). This means that I need to get or
>> create a cache to do a SQL query. What cache name I should use to get
>> instance of IgniteCache? Some dummy cache?
>>
>> Thanks,
>> Maxim
>>
>
>