Oleksandr Onopriienko
Oleksandr Onopriienko

Reputation: 139

Distribute cache between Pods in Kubernetes with Hazelcast in Spring boot application

I have a Spring boot application with Hibernate L2 cache enabled. I integrated Hazelcast as cache provider for Hibernate. My application runs on Kubernetes and deployed on 2 pods.I verified, that hibernate l2 cache successfully distributed between 2 pods, within 1 cluster.So if 1 entity was put in cache in one pod, I see that cache size increases equally in all pods.

Also ,I want to use Hazelcast with spring @Cacheable But this cache is not distributed between 2 pods, and just works separately in each instance. So ,If I trigger method with @Cacheable in first pod, and it's result is cached, then if I try to execute the same method(it result must be already in cache, after execution on fist pod) on second pod, It will not take it's result from cache.

What I need to add to configuration to make this work as expected? Would be very grateful for any advice.Thanks.

deployment.yaml for Kubernetes

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: hazelcast-cluster-role
rules:
  - apiGroups:
      - ""
    resources:
      - endpoints
      - pods
      - nodes
      - services
    verbs:
      - get
      - list

---

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: hazelcast-cluster-role-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: hazelcast-cluster-role
subjects:
  - kind: ServiceAccount
    name: default
    namespace: default

Configuration for Hazelcast - hazelcast.yaml

hazelcast:
  instance-name: my-instance
  network:
    join:
      multicast:
        enabled: false
      kubernetes:
        enabled: true
        namespace: dev

Application properties

spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.url=jdbc:postgresql://host.docker.internal/postgres
spring.datasource.username=postgres
spring.datasource.password=pass
spring.datasource.driver-class-name=org.postgresql.Driver

spring.jpa.properties.hibernate.cache.use_second_level_cache=true
spring.jpa.properties.hibernate.cache.region.factory_class=com.hazelcast.hibernate.HazelcastCacheRegionFactory

spring.jpa.hibernate.ddl-auto=create-drop
spring.jpa.properties.hibernate.show_sql=true

hibernate.cache.hazelcast.instance_name=my-instance

Service with @Cacheable

@Service
public class BookService {

    @Autowired
    private BookRepo bookRepo;

    @Cacheable("books")
    public Iterable<Book> getBooks() {
        try {
            Thread.sleep(15000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException(e);
        }
        System.out.println("Book service triggered");
        return bookRepo.findAll();
    }

}

Upvotes: 0

Views: 4072

Answers (1)

Neil Stevenson
Neil Stevenson

Reputation: 3150

If I understand correctly, the data that is to be shared does not use @Cacheable, which makes it simpler, solution 1 below.

Solution 1 - Each pod/JVM contains 2 Hazelcast instances, one is a cache manager.

  • The first is used for data to share with other pods, and uses Kubernetes discovery, as you already have.
  • The second is wired as the cache manager implementation. If this second instance is configured with discovery de-activated, it won't find other instances to share @Cacheable data with.
  • Be sure the second instance has a different cluster name and port range from the first.

Solution 1 - Each pod/JVM contains 2 Hazelcast instances, both are cache managers.

  • Configure both as for solution 1, the first clustered and the second standalone.
  • Use @Cacheable(cacheManager = "first" ...) or @Cacheable(cacheManager = "second" ...) to select what is cached locally and what is cached across all pods.

Example Hazelcast code for 2nd standalone instance in same JVM.

    @Bean(name = "B")
    public HazelcastInstance hazelcastInstanceB() {
        Config config = new Config();
        config.setClusterName(UUID.randomUUID().toString());
        config.getNetworkConfig().setPort(6701);
        config.getNetworkConfig().getJoin().getAutoDetectionConfig().setEnabled(false);
        config.getNetworkConfig().getJoin().getMulticastConfig().setEnabled(false);
        return Hazelcast.newHazelcastInstance(config);
    }
    @Autowired
    @Qualifier(value = "B")
    private HazelcastInstance hazelcastInstanceB;

Upvotes: 1

Related Questions