hazelcast-client
Version: 
Hazelcast - open source In-Memory Data Grid - client for NodeJS
238 lines • 9.71 kB
JavaScript
"use strict";
/*
 * Copyright (c) 2008-2018, 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.
 */
Object.defineProperty(exports, "__esModule", { value: true });
var Config_1 = require("../Config");
var Util_1 = require("../Util");
var DataStoreHashMap_1 = require("../DataStoreHashMap");
var AlwaysFreshStaleReadDetectorImpl = require("./AlwaysFreshStaleReadDetectorImpl");
var DataRecord_1 = require("./DataRecord");
var Long = require("long");
var NearCacheImpl = /** @class */ (function () {
    function NearCacheImpl(nearCacheConfig, serializationService) {
        this.staleReadDetector = AlwaysFreshStaleReadDetectorImpl.INSTANCE;
        this.reservationCounter = Long.ZERO;
        this.evictedCount = 0;
        this.expiredCount = 0;
        this.missCount = 0;
        this.hitCount = 0;
        this.serializationService = serializationService;
        this.name = nearCacheConfig.name;
        this.invalidateOnChange = nearCacheConfig.invalidateOnChange;
        this.maxIdleSeconds = nearCacheConfig.maxIdleSeconds;
        this.inMemoryFormat = nearCacheConfig.inMemoryFormat;
        this.timeToLiveSeconds = nearCacheConfig.timeToLiveSeconds;
        this.evictionPolicy = nearCacheConfig.evictionPolicy;
        this.evictionMaxSize = nearCacheConfig.evictionMaxSize;
        this.evictionSamplingCount = nearCacheConfig.evictionSamplingCount;
        this.evictionSamplingPoolSize = nearCacheConfig.evictionSamplingPoolSize;
        if (this.evictionPolicy === Config_1.EvictionPolicy.LFU) {
            this.compareFunc = DataRecord_1.DataRecord.lfuComp;
        }
        else if (this.evictionPolicy === Config_1.EvictionPolicy.LRU) {
            this.compareFunc = DataRecord_1.DataRecord.lruComp;
        }
        else if (this.evictionPolicy === Config_1.EvictionPolicy.RANDOM) {
            this.compareFunc = DataRecord_1.DataRecord.randomComp;
        }
        else {
            this.compareFunc = undefined;
        }
        this.evictionCandidatePool = [];
        this.internalStore = new DataStoreHashMap_1.DataKeyedHashMap();
    }
    NearCacheImpl.prototype.nextReservationId = function () {
        var res = this.reservationCounter;
        this.reservationCounter = this.reservationCounter.add(1);
        return res;
    };
    NearCacheImpl.prototype.tryReserveForUpdate = function (key) {
        var internalRecord = this.internalStore.get(key);
        var resId = this.nextReservationId();
        if (internalRecord === undefined) {
            this.doEvictionIfRequired();
            var dr = new DataRecord_1.DataRecord(key, undefined, undefined, this.timeToLiveSeconds);
            dr.casStatus(DataRecord_1.DataRecord.READ_PERMITTED, resId);
            this.internalStore.set(key, dr);
            return resId;
        }
        if (internalRecord.casStatus(DataRecord_1.DataRecord.READ_PERMITTED, resId)) {
            return resId;
        }
        return DataRecord_1.DataRecord.NOT_RESERVED;
    };
    NearCacheImpl.prototype.tryPublishReserved = function (key, value, reservationId) {
        var internalRecord = this.internalStore.get(key);
        if (internalRecord && internalRecord.casStatus(reservationId, DataRecord_1.DataRecord.READ_PERMITTED)) {
            if (this.inMemoryFormat === Config_1.InMemoryFormat.OBJECT) {
                internalRecord.value = this.serializationService.toObject(value);
            }
            else {
                internalRecord.value = this.serializationService.toData(value);
            }
            internalRecord.setCreationTime();
            this.initInvalidationMetadata(internalRecord);
        }
        else if (internalRecord === undefined) {
            return undefined;
        }
        else {
            if (this.inMemoryFormat === Config_1.InMemoryFormat.BINARY) {
                return this.serializationService.toObject(internalRecord.value);
            }
            else {
                return internalRecord.value;
            }
        }
    };
    NearCacheImpl.prototype.setStaleReadDetector = function (staleReadDetector) {
        this.staleReadDetector = staleReadDetector;
    };
    /**
     * Creates a new {DataRecord} for given key and value. Then, puts the record in near cache.
     * If the number of records in near cache exceeds {evictionMaxSize}, it removes expired items first.
     * If there is no expired item, it triggers an invalidation process to create free space.
     * @param key
     * @param value
     */
    NearCacheImpl.prototype.put = function (key, value) {
        this.doEvictionIfRequired();
        if (this.inMemoryFormat === Config_1.InMemoryFormat.OBJECT) {
            value = this.serializationService.toObject(value);
        }
        else {
            value = this.serializationService.toData(value);
        }
        var dr = new DataRecord_1.DataRecord(key, value, undefined, this.timeToLiveSeconds);
        this.initInvalidationMetadata(dr);
        this.internalStore.set(key, dr);
    };
    NearCacheImpl.prototype.initInvalidationMetadata = function (dr) {
        if (this.staleReadDetector === AlwaysFreshStaleReadDetectorImpl.INSTANCE) {
            return;
        }
        var partitionId = this.staleReadDetector.getPartitionId(dr.key);
        var metadataContainer = this.staleReadDetector.getMetadataContainer(partitionId);
        dr.setInvalidationSequence(metadataContainer.getSequence());
        dr.setUuid(metadataContainer.getUuid());
    };
    /**
     *
     * @param key
     * @returns the value if present in near cache, 'undefined' if not
     */
    NearCacheImpl.prototype.get = function (key) {
        var dr = this.internalStore.get(key);
        if (dr === undefined) {
            this.missCount++;
            return undefined;
        }
        if (this.staleReadDetector.isStaleRead(key, dr)) {
            this.internalStore.delete(key);
            this.missCount++;
            return undefined;
        }
        if (dr.isExpired(this.maxIdleSeconds)) {
            this.expireRecord(key);
            this.missCount++;
            return undefined;
        }
        dr.setAccessTime();
        dr.hitRecord();
        this.hitCount++;
        if (this.inMemoryFormat === Config_1.InMemoryFormat.BINARY) {
            return this.serializationService.toObject(dr.value);
        }
        else {
            return dr.value;
        }
    };
    NearCacheImpl.prototype.invalidate = function (key) {
        this.internalStore.delete(key);
    };
    NearCacheImpl.prototype.clear = function () {
        this.internalStore.clear();
    };
    NearCacheImpl.prototype.isEvictionRequired = function () {
        return this.evictionPolicy !== Config_1.EvictionPolicy.NONE && this.evictionMaxSize <= this.internalStore.size;
    };
    NearCacheImpl.prototype.doEvictionIfRequired = function () {
        if (!this.isEvictionRequired()) {
            return;
        }
        var internalSize = this.internalStore.size;
        if (this.recomputeEvictionPool() > 0) {
            return;
        }
        else {
            this.evictRecord(this.evictionCandidatePool[0].key);
            this.evictionCandidatePool = this.evictionCandidatePool.slice(1);
        }
    };
    /**
     * @returns number of expired elements.
     */
    NearCacheImpl.prototype.recomputeEvictionPool = function () {
        var arr = Array.from(this.internalStore.values());
        Util_1.shuffleArray(arr);
        var newCandidates = arr.slice(0, this.evictionSamplingCount);
        var cleanedNewCandidates = newCandidates.filter(this.filterExpiredRecord, this);
        var expiredCount = newCandidates.length - cleanedNewCandidates.length;
        if (expiredCount > 0) {
            return expiredCount;
        }
        (_a = this.evictionCandidatePool).push.apply(_a, cleanedNewCandidates);
        this.evictionCandidatePool.sort(this.compareFunc);
        this.evictionCandidatePool = this.evictionCandidatePool.slice(0, this.evictionSamplingPoolSize);
        return 0;
        var _a;
    };
    NearCacheImpl.prototype.filterExpiredRecord = function (candidate) {
        if (candidate.isExpired(this.maxIdleSeconds)) {
            this.expireRecord(candidate.key);
            return false;
        }
        else {
            return true;
        }
    };
    NearCacheImpl.prototype.expireRecord = function (key) {
        if (this.internalStore.delete(key)) {
            this.expiredCount++;
        }
    };
    NearCacheImpl.prototype.evictRecord = function (key) {
        if (this.internalStore.delete(key)) {
            this.evictedCount++;
        }
    };
    NearCacheImpl.prototype.isInvalidatedOnChange = function () {
        return this.invalidateOnChange;
    };
    NearCacheImpl.prototype.getStatistics = function () {
        var stats = {
            evictedCount: this.evictedCount,
            expiredCount: this.expiredCount,
            missCount: this.missCount,
            hitCount: this.hitCount,
            entryCount: this.internalStore.size
        };
        return stats;
    };
    return NearCacheImpl;
}());
exports.NearCacheImpl = NearCacheImpl;
//# sourceMappingURL=NearCache.js.map