You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ignite.apache.org by "Alexey Kukushkin (Jira)" <ji...@apache.org> on 2020/04/14 20:11:00 UTC

[jira] [Created] (IGNITE-12898) Server node with CacheStore fails to re-join the cluster: IgniteCheckedException: Cannot enable read-through (loader or store is not provided) for cache

Alexey Kukushkin created IGNITE-12898:
-----------------------------------------

             Summary: Server node with CacheStore fails to re-join the cluster: IgniteCheckedException: Cannot enable read-through (loader or store is not provided) for cache
                 Key: IGNITE-12898
                 URL: https://issues.apache.org/jira/browse/IGNITE-12898
             Project: Ignite
          Issue Type: Bug
    Affects Versions: 2.8
            Reporter: Alexey Kukushkin


If a cache with external persistence is dynamically created on a non-affinity node then the cache affinity node cannot join the cluster after restart.
h2. Repro Steps
 # Run an "empty" Ignite node where no cache is going to be started
 # Run a cache affinity node having the "ROLE" attribute set to "DATA"
 # Create the cache from the "empty" node and use a Node Filter to limit the cache to the "data" node. External persistence is configured for the cache.
 # Restart the "data" node

h3. Actual Result
{{IgniteCheckedException: Cannot enable read-through (loader or store is not provided) for cache}}

h2. Reproducer
h3. Reproducer.java
{code:java}
public class Reproducer {
    @Test
    public void test() throws Exception {
        final String DB_URL = "jdbc:h2:mem:test";
        final String ENTITY_NAME = "Person";

        Function<String, IgniteConfiguration> igniteCfgFactory = instanceName ->
            new IgniteConfiguration()
                .setIgniteInstanceName(instanceName)
                .setDiscoverySpi(new TcpDiscoverySpi()
                    .setIpFinder(new TcpDiscoveryVmIpFinder().setAddresses(Collections.singleton("127.0.0.1:47500")))
                );

        // 1. Run an "empty" Ignite node where no cache is going to be started
        try (Connection dbConn = DriverManager.getConnection(DB_URL, "sa", "");
             Statement dbStmt = dbConn.createStatement();
             Ignite emptyNode = Ignition.start(igniteCfgFactory.apply("emptyyNode"))) {
            // 2. Run a "Person" cache affinity node having the "ROLE" attribute set to "DATA"
            Map<String, Object> dataNodeAttrs = new HashMap<>(1);
            dataNodeAttrs.put(DataNodeFilter.ATTR_NAME, DataNodeFilter.ATTR_VAL);

            Ignite dataNode = Ignition.start(igniteCfgFactory.apply("dataNode").setUserAttributes(dataNodeAttrs));

            // 3. Create the "Person" cache from the "empty" node and use a Node Filter to limit the cache to the
            // "data" node. External persistence to the "Person" table in H2 DB is configured for the cache.
            dbStmt.execute("CREATE TABLE " + ENTITY_NAME + " (id int PRIMARY KEY, name varchar)");

            CacheJdbcPojoStoreFactory<Integer, BinaryObject> igniteStoreFactory = new CacheJdbcPojoStoreFactory<>();
            igniteStoreFactory.setDataSourceFactory(() -> JdbcConnectionPool.create(DB_URL, "sa", ""))
                .setTypes(
                    new JdbcType()
                        .setCacheName(ENTITY_NAME)
                        .setDatabaseTable(ENTITY_NAME)
                        .setKeyType(Integer.class)
                        .setValueType(ENTITY_NAME)
                        .setKeyFields(new JdbcTypeField(java.sql.Types.INTEGER, "id", Integer.class, "id"))
                        .setValueFields(
                            new JdbcTypeField(java.sql.Types.INTEGER, "id", Integer.class, "id"),
                            new JdbcTypeField(java.sql.Types.VARCHAR, "name", String.class, "name")
                        )
                );

            CacheConfiguration<Integer, BinaryObject> cacheCfg =
                new CacheConfiguration<Integer, BinaryObject>(ENTITY_NAME)
                    .setCacheMode(CacheMode.REPLICATED)
                    .setCacheStoreFactory(igniteStoreFactory)
                    .setWriteThrough(true)
                    .setReadThrough(true)
                    .setNodeFilter(new DataNodeFilter());

            emptyNode.createCache(cacheCfg).withKeepBinary();

            // 4. Restart the "data" node
            dataNode.close();
            dataNode = Ignition.start(igniteCfgFactory.apply("node2").setUserAttributes(dataNodeAttrs));

            dataNode.close();
        }
    }

    private static class DataNodeFilter implements IgnitePredicate<ClusterNode> {
        public static final String ATTR_NAME = "ROLE";
        public static final String ATTR_VAL = "DATA";

        @Override public boolean apply(ClusterNode node) {
            Object role = node.attributes().get(ATTR_NAME);
            return role != null && ATTR_VAL.equalsIgnoreCase(role.toString());
        }
    }
}
{code}

h3. build.gradle
{code:groovy}
dependencies {
    compile group: 'org.apache.ignite', name: 'ignite-core', version: '2.8.0'
    compile group: 'com.h2database', name: 'h2', version: '1.4.200'

    testCompile group: 'junit', name: 'junit', version: '4.12'
}
{code}

h2. Workaround
Create dynamic caches only on the affinity nodes or use "static" caches defined in ignite node configuration.

h2. Stack Trace
{code}
class org.apache.ignite.IgniteCheckedException: Cannot enable read-through (loader or store is not provided) for cache: Person
	at org.apache.ignite.internal.processors.cache.ValidationOnNodeJoinUtils.validate(ValidationOnNodeJoinUtils.java:348)
	at org.apache.ignite.internal.processors.cache.GridCacheProcessor.createCacheContext(GridCacheProcessor.java:1201)
	at org.apache.ignite.internal.processors.cache.GridCacheProcessor.prepareCacheContext(GridCacheProcessor.java:1995)
	at org.apache.ignite.internal.processors.cache.GridCacheProcessor.lambda$prepareStartCaches$d40a1773$1(GridCacheProcessor.java:1830)
	at org.apache.ignite.internal.processors.cache.GridCacheProcessor.lambda$prepareStartCaches$8(GridCacheProcessor.java:1754)
	at org.apache.ignite.internal.processors.cache.GridCacheProcessor.lambda$prepareStartCaches$926b6886$1(GridCacheProcessor.java:1827)
	at org.apache.ignite.internal.util.IgniteUtils.doInParallel(IgniteUtils.java:11157)
	at org.apache.ignite.internal.util.IgniteUtils.doInParallel(IgniteUtils.java:11059)
	at org.apache.ignite.internal.processors.cache.GridCacheProcessor.prepareStartCaches(GridCacheProcessor.java:1822)
	at org.apache.ignite.internal.processors.cache.GridCacheProcessor.prepareStartCaches(GridCacheProcessor.java:1753)
	at org.apache.ignite.internal.processors.cache.GridCacheProcessor.startCachesOnLocalJoin(GridCacheProcessor.java:1699)
	at org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture.initCachesOnLocalJoin(GridDhtPartitionsExchangeFuture.java:994)
	at org.apache.ignite.internal.processors.cache.distributed.dht.preloader.GridDhtPartitionsExchangeFuture.init(GridDhtPartitionsExchangeFuture.java:847)
	at org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$ExchangeWorker.body0(GridCachePartitionExchangeManager.java:3172)
	at org.apache.ignite.internal.processors.cache.GridCachePartitionExchangeManager$ExchangeWorker.body(GridCachePartitionExchangeManager.java:3021)
	at org.apache.ignite.internal.util.worker.GridWorker.run(GridWorker.java:120)
	at java.base/java.lang.Thread.run(Thread.java:834)
{code}



--
This message was sent by Atlassian Jira
(v8.3.4#803005)