Search code examples
javaignitegridgain

Apache Ignite Cache Store + HikariCP DataSource


I am trying to set up Apache Ignite cache store using PostgreSQL as an external storage.

public class MyCacheStore extends CacheStoreAdapter<String, MyCache> {

    private static final String GET_QUERY= "SELECT * FROM ..";

    private static final String UPDATE_QUERY = "UPDATE ...";

    private static final String DELETE_QUERY = "DELETE FROM ..";

    @CacheStoreSessionResource
    private CacheStoreSession session;

    @Override
    public MyCache load(String key) throws CacheLoaderException {
        Connection connection = session.attachment();

        try (PreparedStatement preparedStatement = connection.prepareStatement(GET_QUERY)) {
           // some stuff
        }

    }

    @Override
    public void loadCache(IgniteBiInClosure<String, MyCache> clo, Object... args) {
        super.loadCache(clo, args);
    }

    @Override
    public void write(Cache.Entry<? extends String, ? extends MyCache> entry) throws CacheWriterException {
        Connection connection = session.attachment();

        try (PreparedStatement preparedStatement = connection.prepareStatement(UPDATE_QUERY)) {
            // some stuff
        } 

    }

    @Override
    public void delete(Object key) throws CacheWriterException {
        Connection connection = session.attachment();

        try (PreparedStatement preparedStatement = connection.prepareStatement(DELETE_QUERY)) {
            // some stuff
        }
    }
}

MyCache is a standard class:

public class MyCache implements Serializable {

    @QuerySqlField(index = true, name = "id")
    private String id;

    public MyCache() {

    }

    public MyCache(String id) {
        this.id = id;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

}

Here is a configuration class

import javax.cache.configuration.Factory;
import javax.cache.configuration.FactoryBuilder;

@Configuration
public class ServiceConfig {

    // no problems here
    @Bean
    @ConfigurationProperties(prefix = "postgre")
    DataSource dataSource() {
        return DataSourceBuilder
                .create()
                .build();
    }

    @Bean
    public Ignite igniteInstance(IgniteConfiguration igniteConfiguration) {
        return Ignition.start(igniteConfiguration);
    }

    @Bean
    public IgniteConfiguration igniteCfg () {
        // some other stuff here

        IgniteConfiguration cfg = new IgniteConfiguration();
        cfg.setClientMode(true);

        CacheConfiguration myCacheConfiguration = new CacheConfiguration("MY_CACHE")
                    .setIndexedTypes(String.class, MyCache.class)
                    .setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL)
                    .setReadThrough(true)
                    .setReadThrough(true)
                    .setCacheStoreSessionListenerFactories(new MyCacheStoreSessionListenerFactory(dataSource))
                    .setCacheStoreFactory(FactoryBuilder.factoryOf(MyCacheStore.class));

        cfg.setCacheConfiguration(myCacheConfiguration);

        return cfg;
    }

    private static class MyCacheStoreSessionListenerFactory implements Factory {

        DataSource dataSource;

        MyCacheStoreSessionListenerFactory(DataSource dataSource) {
            this.dataSource = dataSource;
        }

        @Override
        public CacheStoreSessionListener create() {
            // Data Source
            CacheJdbcStoreSessionListener listener = new CacheJdbcStoreSessionListener();
            listener.setDataSource(dataSource);

            return listener;
        }
    }
}

And this is what I get in logs:

...
Caused by: class org.apache.ignite.IgniteCheckedException: Failed to validate cache configuration 
(make sure all objects in cache configuration are serializable): MyCache
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor$11.applyx(GridCacheProcessor.java:4766)
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor$11.applyx(GridCacheProcessor.java:4743)
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor.withBinaryContext(GridCacheProcessor.java:4788)
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor.cloneCheckSerializable(GridCacheProcessor.java:4743)
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor.addCacheOnJoin(GridCacheProcessor.java:818)
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor.addCacheOnJoinFromConfig(GridCacheProcessor.java:891)
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor.startCachesOnStart(GridCacheProcessor.java:753)
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor.start(GridCacheProcessor.java:795)
    at org.apache.ignite.internal.IgniteKernal.startProcessor(IgniteKernal.java:1700)
    ... 77 more
Caused by: class org.apache.ignite.IgniteCheckedException: Failed to serialize object: CacheConfiguration [name=MyCache, grpName=null, memPlcName=null, storeConcurrentLoadAllThreshold=5, rebalancePoolSize=2, rebalanceTimeout=10000, evictPlc=null, evictPlcFactory=null, onheapCache=false, sqlOnheapCache=false, sqlOnheapCacheMaxSize=0, evictFilter=null, eagerTtl=true, dfltLockTimeout=0, nearCfg=null, writeSync=null, storeFactory=javax.cache.configuration.FactoryBuilder$ClassFactory@d87782a1, storeKeepBinary=false, loadPrevVal=false, aff=null, cacheMode=PARTITIONED, atomicityMode=TRANSACTIONAL, backups=0, invalidate=false, tmLookupClsName=null, rebalanceMode=ASYNC, rebalanceOrder=0, rebalanceBatchSize=524288, rebalanceBatchesPrefetchCnt=2, maxConcurrentAsyncOps=500, sqlIdxMaxInlineSize=-1, writeBehindEnabled=false, writeBehindFlushSize=10240, writeBehindFlushFreq=5000, writeBehindFlushThreadCnt=1, writeBehindBatchSize=512, writeBehindCoalescing=true, maxQryIterCnt=1024, affMapper=null, rebalanceDelay=0, rebalanceThrottle=0, interceptor=null, longQryWarnTimeout=3000, qryDetailMetricsSz=0, readFromBackup=true, nodeFilter=null, sqlSchema=null, sqlEscapeAll=false, cpOnRead=true, topValidator=null, partLossPlc=IGNORE, qryParallelism=1, evtsDisabled=false, encryptionEnabled=false]
    at org.apache.ignite.marshaller.jdk.JdkMarshaller.marshal0(JdkMarshaller.java:103)
    at org.apache.ignite.marshaller.AbstractNodeNameAwareMarshaller.marshal(AbstractNodeNameAwareMarshaller.java:70)
    at org.apache.ignite.marshaller.jdk.JdkMarshaller.marshal0(JdkMarshaller.java:117)
    at org.apache.ignite.marshaller.AbstractNodeNameAwareMarshaller.marshal(AbstractNodeNameAwareMarshaller.java:58)
    at org.apache.ignite.internal.util.IgniteUtils.marshal(IgniteUtils.java:10250)
    at org.apache.ignite.internal.processors.cache.GridCacheProcessor$11.applyx(GridCacheProcessor.java:4762)
    ... 85 more
Caused by: java.io.NotSerializableException: com.zaxxer.hikari.HikariDataSource
    at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185)
    at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)

I have read all official documentation about it and examined many other examples, but can't make it run.

HikariCP is the most popular connection pool library, I can't understand why Ignite throws an exception about not being able to serialize DataSource.
Any advice or idea would be appreciated, thank you!


Solution

  • Since your Cache Store is not serializable, you should not use Factory.factoryOf (which is a no-op wrapper) but instead supply a real serializable factory implementation which will acquire local HikariCP on node and then construct the Cache Store.