Search code examples
hazelcasthazelcast-imap

How to prevent duplicate tasks when run same IScheduledExecutorService on apps in cluster?


I want to understand difference between hazelcast methods for IScheduledExecutorService for prevent duplicate tasks. I have two java app with HazelcastInstance. Respectively I have hazelcast cluster with two HazelcastInstances (servers). I use IMap and want to reset AtomicLong every midnight.

    config.getScheduledExecutorConfig("my scheduler")
            .setPoolSize(16)
            .setCapacity(100)
            .setDurability(1);


class DelayedResetTask implements Runnable, HazelcastInstanceAware, Serializable {

    static final long serialVersionUID = -7588380448693010399L;

    private transient HazelcastInstance client;

    @Override
    public void run() {
        final IMap<Long, AtomicLong> map = client.getMap(HazelcastConfiguration.mapName);

        final ILogger logger = client.getLoggingService().getLogger(HazelcastInstance.class);
        logger.info("Show data in cache before reset: " + map.entrySet());
        map.keySet().forEach(key -> map.put(key, new AtomicLong(0)));
        logger.info("Data was reseted: " + map.entrySet());
    }

    @Override
    public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { this.client = hazelcastInstance; }
}

private void resetAtMidnight() {
    final Long midnight = LocalDateTime.now().until(LocalDate.now().plusDays(1).atStartOfDay(), ChronoUnit.MINUTES);

    executor.scheduleAtFixedRate(new DelayedResetTask(), midnight, TimeUnit.DAYS.toMinutes(1), TimeUnit.MINUTES);
}

I don't want to execute this task on each instance in parallel. After reading documentation documentation I don't understand how I can execute reset in both servers for one step (without duplicate tasks, without execution on both servers at one time).
What method I can use for my task scheduleOnAllMembersAtFixedRate or scheduleAtFixedRate or scheduleOnMembersAtFixedRate. How to prevent duplicate tasks when run same IScheduledExecutorService on apps in cluster?


Solution

  • You need to run your code only once in the cluster, since the map you are resetting can be accessed from any member. Both members access to the same map instance, only the entries are kept in different members. You can use scheduleAtFixedRate to run it once.

    Additionally, you do not need to call IMap#keySet().forEach() to traverse over all entries in the map. Instead, you can use EntryProcessor as below:

        public static class DelayedResetTask implements Runnable, HazelcastInstanceAware, Serializable {
    
        static final long serialVersionUID = -7588380448693010399L;
    
        private transient HazelcastInstance client;
    
        @Override
        public void run() {
            final IMap<Long, AtomicLong> map = client.getMap(HazelcastConfiguration.mapName);
    
            final ILogger logger = client.getLoggingService().getLogger(HazelcastInstance.class);
            logger.info("Show data in cache before reset: " + map.entrySet());
            map.executeOnEntries(new AbstractEntryProcessor() {
                @Override
                public Object process(Map.Entry entry) {
                    entry.setValue(new AtomicLong(0));
                    return null;
                }
            });
            logger.info("Data was reseted: " + map.entrySet());
        }
    
        @Override
        public void setHazelcastInstance(HazelcastInstance hazelcastInstance) { this.client = hazelcastInstance; }