HazelcastMonitoringService.java

package com.tradecloud.config;

import com.hazelcast.config.Config;
import com.hazelcast.config.MapConfig;
import com.hazelcast.core.DistributedObject;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.map.LocalMapStats;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.DependsOn;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

@Component
@DependsOn("hazelcastInstance")
public class HazelcastMonitoringService {
    private static final Logger log = Logger.getLogger(HazelcastMonitoringService.class);

    @Autowired
    private HazelcastInstance hazelcastInstance;

    @PostConstruct
    public void clearAllMapsOnStartup() {
        log.info("Starting Hazelcast startup cleanup...");

        String lockKey = "hz-startup-clear-lock";
        IMap<String, Boolean> lockMap = hazelcastInstance.getMap("startup-lock-map");

        // try to acquire "lock"
        Boolean acquired = lockMap.putIfAbsent(lockKey, true);
        if (acquired == null) { // we got the lock
            try {
                hazelcastInstance.getDistributedObjects().forEach(obj -> {
                    if (obj instanceof IMap<?, ?> map) {
                        log.info("Clearing Hazelcast map: " + map.getName());
                        map.clear();
                    }
                });
                log.info("Hazelcast startup cleanup completed.");
            } finally {
                lockMap.delete(lockKey); // release lock
            }
        } else {
            log.info("Another node is already doing startup cleanup. Skipping.");
        }
    }

    @PreDestroy
    public void onShutdown() {
        // This stops the heartbeat threads immediately
        // before the WebappClassLoader is destroyed
        if (hazelcastInstance != null) {
            log.info("Hazelcast proper shutdown..");
            hazelcastInstance.getLifecycleService().shutdown();
        }
    }

    // Runs every 5 minutes (300,000 milliseconds)
    //@Scheduled(fixedRate = 120_000)
    public void runMemoryAudit() {
        Config config = hazelcastInstance.getConfig();
        long totalClusterHeap = 0;

        log.info("========== HAZELCAST PERIODIC AUDIT ==========");

        for (DistributedObject obj : hazelcastInstance.getDistributedObjects()) {
            if (obj instanceof IMap map) {
                MapConfig mCfg = config.findMapConfig(map.getName());
                LocalMapStats stats = map.getLocalMapStats();

                long heapCost = stats.getHeapCost();
                totalClusterHeap += heapCost;

                // Log Config vs Actuals
                log.info(String.format(
                        "MAP: %-30s | Max: %d | Current: %d | Format: %s | Memory: %.2f MB",
                        map.getName(),
                        mCfg.getEvictionConfig().getSize(),
                        stats.getOwnedEntryCount(),
                        mCfg.getInMemoryFormat(),
                        heapCost / (1024.0 * 1024.0)
                ));
            }

            // JVM Context
            long maxMemory = Runtime.getRuntime().maxMemory();
            long totalMemory = Runtime.getRuntime().totalMemory();
            long freeMemory = Runtime.getRuntime().freeMemory();
            long usedMemory = totalMemory - freeMemory;

            log.info(String.format("TOTAL HZ USAGE: %.2f MB", totalClusterHeap / (1024.0 * 1024.0)));
            log.info(String.format("JVM HEAP: Used: %.2f MB / Max: %.2f MB (%.1f%%)",
                    usedMemory / (1024.0 * 1024.0),
                    maxMemory / (1024.0 * 1024.0),
                    (usedMemory * 100.0 / maxMemory)));
            log.info("===============================================");
        }
    }
}