All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.map.impl.LocalMapStatsProvider Maven / Gradle / Ivy

/*
 * Copyright (c) 2008-2016, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.map.impl;

import com.hazelcast.cache.impl.nearcache.NearCache;
import com.hazelcast.cluster.ClusterService;
import com.hazelcast.instance.GroupProperty;
import com.hazelcast.logging.ILogger;
import com.hazelcast.map.impl.nearcache.NearCacheProvider;
import com.hazelcast.map.impl.record.Record;
import com.hazelcast.map.impl.record.RecordStatistics;
import com.hazelcast.map.impl.recordstore.RecordStore;
import com.hazelcast.monitor.NearCacheStats;
import com.hazelcast.monitor.impl.LocalMapStatsImpl;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.partition.InternalPartition;
import com.hazelcast.partition.InternalPartitionService;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.util.ConcurrencyUtil;
import com.hazelcast.util.ConstructorFunction;
import com.hazelcast.util.ExceptionUtil;

import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.TimeUnit;

/**
 * Provides node local statistics of a map via {@link #createLocalMapStats}
 * and also holds all {@link com.hazelcast.monitor.impl.LocalMapStatsImpl} implementations of all maps.
 */
public class LocalMapStatsProvider {

    protected static final int WAIT_PARTITION_TABLE_UPDATE_MILLIS = 100;
    protected static final int RETRY_COUNT = 3;

    protected final ConcurrentMap statsMap
            = new ConcurrentHashMap(1000);
    protected final ConstructorFunction constructorFunction
            = new ConstructorFunction() {
        public LocalMapStatsImpl createNew(String key) {
            return new LocalMapStatsImpl();
        }
    };

    protected final MapServiceContext mapServiceContext;
    protected final NearCacheProvider nearCacheProvider;
    protected final ClusterService clusterService;
    protected final InternalPartitionService partitionService;
    protected final ILogger logger;

    protected final boolean iterateStats;

    public LocalMapStatsProvider(MapServiceContext mapServiceContext) {
        this.mapServiceContext = mapServiceContext;
        NodeEngine nodeEngine = mapServiceContext.getNodeEngine();
        this.logger = nodeEngine.getLogger(getClass());
        this.nearCacheProvider = mapServiceContext.getNearCacheProvider();
        this.clusterService = nodeEngine.getClusterService();
        this.partitionService = nodeEngine.getPartitionService();
        this.iterateStats = nodeEngine.getGroupProperties().getBoolean(GroupProperty.ITERATING_MAP_STATS_ENABLED);
    }

    public LocalMapStatsImpl getLocalMapStatsImpl(String name) {
        return ConcurrencyUtil.getOrPutIfAbsent(statsMap, name, constructorFunction);
    }

    public void destroyLocalMapStatsImpl(String name) {
        statsMap.remove(name);
    }

    public LocalMapStatsImpl createLocalMapStats(String mapName) {
        MapContainer mapContainer = mapServiceContext.getMapContainer(mapName);
        LocalMapStatsImpl stats = getLocalMapStatsImpl(mapName);
        if (!mapContainer.getMapConfig().isStatisticsEnabled()) {
            return stats;
        }
        int backupCount = mapContainer.getTotalBackupCount();
        Address thisAddress = clusterService.getThisAddress();

        LocalMapOnDemandCalculatedStats onDemandStats = new LocalMapOnDemandCalculatedStats();
        onDemandStats.setBackupCount(backupCount);

        addNearCacheStats(stats, onDemandStats, mapContainer);

        for (int partitionId = 0; partitionId < partitionService.getPartitionCount(); partitionId++) {
            InternalPartition partition = partitionService.getPartition(partitionId);
            Address owner = partition.getOwnerOrNull();
            if (owner == null) {
                //no-op because no owner is set yet. Therefore we don't know anything about the map
                continue;
            }

            if (owner.equals(thisAddress)) {
                addOwnerPartitionStats(stats, onDemandStats, mapName, partitionId);
            } else {
                addReplicaPartitionStats(onDemandStats, mapName, partitionId,
                        partition, partitionService, backupCount, thisAddress);
            }
        }

        onDemandStats.copyValuesTo(stats);

        return stats;
    }

    /**
     * Calculates and adds owner partition stats.
     */
    protected void addOwnerPartitionStats(LocalMapStatsImpl stats,
                                          LocalMapOnDemandCalculatedStats onDemandStats,
                                          String mapName, int partitionId) {
        RecordStore recordStore = getRecordStoreOrNull(mapName, partitionId);
        if (!hasRecords(recordStore)) {
            return;
        }
        int lockedEntryCount = 0;
        long lastAccessTime = 0;
        long lastUpdateTime = 0;
        long hits = 0;

        if (iterateStats) {
            Iterator iterator = recordStore.iterator();
            while (iterator.hasNext()) {
                Record record = iterator.next();
                Data key = record.getKey();

                hits += getHits(record);
                lockedEntryCount += isLocked(key, recordStore);
                lastAccessTime = Math.max(lastAccessTime, record.getLastAccessTime());
                lastUpdateTime = Math.max(lastUpdateTime, record.getLastUpdateTime());
            }
        }

        onDemandStats.incrementLockedEntryCount(lockedEntryCount);
        onDemandStats.incrementHits(hits);
        onDemandStats.incrementDirtyEntryCount(recordStore.getMapDataStore().notFinishedOperationsCount());
        onDemandStats.incrementOwnedEntryMemoryCost(recordStore.getHeapCost());
        onDemandStats.incrementHeapCost(recordStore.getHeapCost());
        onDemandStats.incrementOwnedEntryCount(recordStore.size());

        stats.setLastAccessTime(lastAccessTime);
        stats.setLastUpdateTime(lastUpdateTime);
    }

    protected long getHits(Record record) {
        RecordStatistics stats = record.getStatistics();
        return stats.getHits();
    }

    /**
     * Return 1 if locked, otherwise 0.
     * Used to find {@link LocalMapStatsImpl#lockedEntryCount}.
     */
    protected int isLocked(Data key, RecordStore recordStore) {
        if (recordStore.isLocked(key)) {
            return 1;
        }
        return 0;
    }

    /**
     * Calculates and adds replica partition stats.
     */
    protected void addReplicaPartitionStats(LocalMapOnDemandCalculatedStats onDemandStats,
                                            String mapName, int partitionId, InternalPartition partition,
                                            InternalPartitionService partitionService, int backupCount, Address thisAddress) {
        long backupEntryCount = 0;
        long backupEntryMemoryCost = 0;

        for (int replica = 1; replica <= backupCount; replica++) {
            Address replicaAddress = getReplicaAddress(replica, partition, partitionService, backupCount);
            if (!isReplicaAvailable(replicaAddress, partitionService, backupCount)) {
                printWarning(partition, replica);
                continue;
            }
            if (isReplicaOnThisNode(replicaAddress, thisAddress)) {
                RecordStore recordStore = getRecordStoreOrNull(mapName, partitionId);
                if (hasRecords(recordStore)) {
                    backupEntryMemoryCost += recordStore.getHeapCost();
                    backupEntryCount += recordStore.size();
                }
            }
        }
        onDemandStats.incrementHeapCost(backupEntryMemoryCost);
        onDemandStats.incrementBackupEntryMemoryCost(backupEntryMemoryCost);
        onDemandStats.incrementBackupEntryCount(backupEntryCount);
    }

    protected boolean hasRecords(RecordStore recordStore) {
        return recordStore != null && recordStore.size() > 0;
    }

    protected boolean isReplicaAvailable(Address replicaAddress, InternalPartitionService partitionService, int backupCount) {
        return !(replicaAddress == null && partitionService.getMemberGroupsSize() > backupCount);
    }

    protected boolean isReplicaOnThisNode(Address replicaAddress, Address thisAddress) {
        return replicaAddress != null && replicaAddress.equals(thisAddress);
    }

    protected void printWarning(InternalPartition partition, int replica) {
        logger.warning("Partition: " + partition + ", replica: " + replica + " has no owner!");
    }


    protected RecordStore getRecordStoreOrNull(String mapName, int partitionId) {
        final PartitionContainer partitionContainer = mapServiceContext.getPartitionContainer(partitionId);
        return partitionContainer.getExistingRecordStore(mapName);
    }

    /**
     * Gets replica address. Waits if necessary.
     *
     * @see #waitForReplicaAddress
     */
    protected Address getReplicaAddress(int replica, InternalPartition partition, InternalPartitionService partitionService,
                                        int backupCount) {
        Address replicaAddress = partition.getReplicaAddress(replica);
        if (replicaAddress == null) {
            replicaAddress = waitForReplicaAddress(replica, partition, partitionService, backupCount);
        }
        return replicaAddress;
    }

    /**
     * Waits partition table update to get replica address if current replica address is null.
     */
    protected Address waitForReplicaAddress(int replica, InternalPartition partition, InternalPartitionService partitionService,
                                            int backupCount) {
        int tryCount = RETRY_COUNT;
        Address replicaAddress = null;
        while (replicaAddress == null && partitionService.getMemberGroupsSize() > backupCount && tryCount-- > 0) {
            sleep();
            replicaAddress = partition.getReplicaAddress(replica);
        }
        return replicaAddress;
    }

    protected void sleep() {
        try {
            TimeUnit.MILLISECONDS.sleep(WAIT_PARTITION_TABLE_UPDATE_MILLIS);
        } catch (InterruptedException e) {
            throw ExceptionUtil.rethrow(e);
        }
    }

    /**
     * Adds near cache stats.
     */
    protected void addNearCacheStats(LocalMapStatsImpl stats,
                                     LocalMapOnDemandCalculatedStats onDemandStats, MapContainer mapContainer) {
        if (!mapContainer.getMapConfig().isNearCacheEnabled()) {
            return;
        }
        NearCache nearCache = nearCacheProvider.getOrCreateNearCache(mapContainer.getName());
        NearCacheStats nearCacheStats = nearCache.getNearCacheStats();
        long nearCacheHeapCost = mapContainer.getNearCacheSizeEstimator().getSize();

        stats.setNearCacheStats(nearCacheStats);
        onDemandStats.incrementHeapCost(nearCacheHeapCost);
    }

    protected static class LocalMapOnDemandCalculatedStats {

        protected long hits;

        protected long ownedEntryCount;
        protected long backupEntryCount;
        protected long ownedEntryMemoryCost;
        protected long backupEntryMemoryCost;
        /**
         * Holds total heap cost of map & near-cache & backups.
         */
        protected long heapCost;
        protected long lockedEntryCount;
        protected long dirtyEntryCount;
        protected int backupCount;

        public void setBackupCount(int backupCount) {
            this.backupCount = backupCount;
        }

        public void incrementHits(long hits) {
            this.hits += hits;
        }

        public void incrementOwnedEntryCount(long ownedEntryCount) {
            this.ownedEntryCount += ownedEntryCount;
        }

        public void incrementBackupEntryCount(long backupEntryCount) {
            this.backupEntryCount += backupEntryCount;
        }

        public void incrementOwnedEntryMemoryCost(long ownedEntryMemoryCost) {
            this.ownedEntryMemoryCost += ownedEntryMemoryCost;
        }

        public void incrementBackupEntryMemoryCost(long backupEntryMemoryCost) {
            this.backupEntryMemoryCost += backupEntryMemoryCost;
        }

        public void incrementLockedEntryCount(long lockedEntryCount) {
            this.lockedEntryCount += lockedEntryCount;
        }

        public void incrementDirtyEntryCount(long dirtyEntryCount) {
            this.dirtyEntryCount += dirtyEntryCount;
        }

        public void incrementHeapCost(long heapCost) {
            this.heapCost += heapCost;
        }

        public void copyValuesTo(LocalMapStatsImpl stats) {
            stats.setBackupCount(backupCount);
            stats.setHits(hits);
            stats.setOwnedEntryCount(ownedEntryCount);
            stats.setBackupEntryCount(backupEntryCount);
            stats.setOwnedEntryMemoryCost(ownedEntryMemoryCost);
            stats.setBackupEntryMemoryCost(backupEntryMemoryCost);
            stats.setHeapCost(heapCost);
            stats.setLockedEntryCount(lockedEntryCount);
            stats.setDirtyEntryCount(dirtyEntryCount);
        }

    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy