Package com.hazelcast.map

Source Code of com.hazelcast.map.DefaultRecordStore

/*
* Copyright (c) 2008-2013, 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;

import com.hazelcast.concurrent.lock.LockService;
import com.hazelcast.concurrent.lock.LockStore;
import com.hazelcast.core.EntryView;
import com.hazelcast.map.mapstore.MapDataStore;
import com.hazelcast.map.merge.MapMergePolicy;
import com.hazelcast.map.record.Record;
import com.hazelcast.nio.serialization.Data;
import com.hazelcast.query.impl.IndexService;
import com.hazelcast.spi.DefaultObjectNamespace;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.exception.RetryableHazelcastException;
import com.hazelcast.util.ExceptionUtil;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
* Default implementation of record-store.
*/
public class DefaultRecordStore extends AbstractEvictableRecordStore implements RecordStore {

    private final LockStore lockStore;
    private final MapDataStore<Data, Object> mapDataStore;
    private final RecordStoreLoader recordStoreLoader;

    public DefaultRecordStore(MapContainer mapContainer, int partitionId) {
        super(mapContainer, partitionId);
        this.lockStore = createLockStore();
        this.mapDataStore
                = mapContainer.getMapStoreManager().getMapDataStore(partitionId);
        this.recordStoreLoader = createRecordStoreLoader();
        this.recordStoreLoader.loadAllKeys();
    }

    @Override
    public boolean isLoaded() {
        return recordStoreLoader.isLoaded();
    }

    @Override
    public void setLoaded(boolean loaded) {
        recordStoreLoader.setLoaded(loaded);
    }

    @Override
    public void checkIfLoaded() {
        Throwable throwable = null;
        final RecordStoreLoader recordStoreLoader = this.recordStoreLoader;
        final Throwable exception = recordStoreLoader.getExceptionOrNull();
        if (exception == null && !recordStoreLoader.isLoaded()) {
            throwable = new RetryableHazelcastException("Map is not ready!!!");
        } else if (exception != null) {
            throwable = exception;
        }
        if (throwable != null) {
            throw ExceptionUtil.rethrow(throwable);
        }
    }

    @Override
    public void flush() {
        final Collection<Data> processedKeys = mapDataStore.flush();
        for (Data key : processedKeys) {
            final Record record = records.get(key);
            if (record != null) {
                record.onStore();
            }
        }
    }

    @Override
    public Record getRecord(Data key) {
        return records.get(key);
    }

    @Override
    public void putRecord(Data key, Record record) {
        final Record existingRecord = records.put(key, record);
        updateSizeEstimator(-calculateRecordHeapCost(existingRecord));
        updateSizeEstimator(calculateRecordHeapCost(record));
    }

    @Override
    public Record putBackup(Data key, Object value) {
        return putBackup(key, value, DEFAULT_TTL);
    }

    /**
     * @param key   the key to be processed.
     * @param value the value to be processed.
     * @param ttl   milliseconds. Check out {@link com.hazelcast.map.proxy.MapProxySupport#putInternal}
     * @return previous record if exists otherwise null.
     */
    @Override
    public Record putBackup(Data key, Object value, long ttl) {
        final long now = getNow();
        earlyWriteCleanup(now);
        markRecordStoreExpirable(ttl);

        Record record = records.get(key);
        if (record == null) {
            record = createRecord(key, value, ttl, now);
            records.put(key, record);
            updateSizeEstimator(calculateRecordHeapCost(record));
        } else {
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, value, now);
            updateSizeEstimator(calculateRecordHeapCost(record));
        }
        mapDataStore.addBackup(key, value, now);
        return record;
    }

    @Override
    public void deleteRecord(Data key) {
        Record record = records.remove(key);
        if (record != null) {
            record.invalidate();
        }
    }

    @Override
    public Iterator<Record> iterator() {
        return new ReadOnlyRecordIterator(records.values());
    }

    @Override
    public Iterator<Record> loadAwareIterator() {
        checkIfLoaded();
        return iterator();
    }

    @Override
    public Map<Data, Record> getRecordMap() {
        return records;
    }


    @Override
    public void clearPartition() {
        final NodeEngine nodeEngine = mapServiceContext.getNodeEngine();
        final LockService lockService = nodeEngine.getSharedService(LockService.SERVICE_NAME);
        if (lockService != null) {
            final DefaultObjectNamespace namespace = new DefaultObjectNamespace(MapService.SERVICE_NAME, name);
            lockService.clearLockStore(partitionId, namespace);
        }
        final IndexService indexService = mapContainer.getIndexService();
        if (indexService.hasIndex()) {
            for (Data key : records.keySet()) {
                indexService.removeEntryIndex(key);
            }
        }
        clearRecordsMap(Collections.<Data, Record>emptyMap());
        resetSizeEstimator();
        resetAccessSequenceNumber();
        mapDataStore.reset();
    }

    /**
     * Size may not give precise size at a specific moment
     * due to the expiration logic. But eventually, it should be correct.
     *
     * @return record store size.
     */
    @Override
    public int size() {
        // do not add checkIfLoaded(), size() is also used internally
        return records.size();
    }

    @Override
    public boolean isEmpty() {
        checkIfLoaded();
        return records.isEmpty();
    }


    @Override
    public boolean containsValue(Object value) {
        checkIfLoaded();
        final long now = getNow();
        for (Record record : records.values()) {
            if (nullIfExpired(record) == null) {
                continue;
            }
            if (mapServiceContext.compare(name, value, record.getValue())) {
                return true;
            }
        }
        postReadCleanUp(now);
        return false;
    }

    @Override
    public boolean txnLock(Data key, String caller, long threadId, long ttl) {
        checkIfLoaded();
        return lockStore != null && lockStore.txnLock(key, caller, threadId, ttl);
    }

    @Override
    public boolean extendLock(Data key, String caller, long threadId, long ttl) {
        checkIfLoaded();
        return lockStore != null && lockStore.extendLeaseTime(key, caller, threadId, ttl);
    }

    @Override
    public boolean unlock(Data key, String caller, long threadId) {
        checkIfLoaded();
        return lockStore != null && lockStore.unlock(key, caller, threadId);
    }

    @Override
    public boolean forceUnlock(Data dataKey) {
        return lockStore != null && lockStore.forceUnlock(dataKey);
    }

    @Override
    public boolean isLocked(Data dataKey) {
        return lockStore != null && lockStore.isLocked(dataKey);
    }

    @Override
    public boolean canAcquireLock(Data key, String caller, long threadId) {
        return lockStore == null || lockStore.canAcquireLock(key, caller, threadId);
    }

    @Override
    public String getLockOwnerInfo(Data key) {
        return lockStore != null ? lockStore.getOwnerInfo(key) : null;
    }

    @Override
    public Set<Map.Entry<Data, Data>> entrySetData() {
        checkIfLoaded();
        Map<Data, Data> temp = new HashMap<Data, Data>(records.size());
        for (Record record : records.values()) {
            record = nullIfExpired(record);
            if (record == null) {
                continue;
            }
            final Data key = record.getKey();
            final Data value = toData(record.getValue());
            temp.put(key, value);
        }

        return temp.entrySet();
    }

    @Override
    public Map.Entry<Data, Object> getMapEntry(Data dataKey) {
        checkIfLoaded();
        Record record = records.get(dataKey);
        record = nullIfExpired(record);
        if (record == null) {
            record = getRecordInternal(dataKey, true);
        } else {
            accessRecord(record);
        }
        final Object value = record != null ? record.getValue() : null;
        return new AbstractMap.SimpleImmutableEntry<Data, Object>(dataKey, value);
    }


    // TODO Does it need to load from store on backup?
    @Override
    public Map.Entry<Data, Object> getMapEntryForBackup(Data dataKey) {
        checkIfLoaded();
        Record record = records.get(dataKey);
        record = nullIfExpired(record);
        if (record == null) {
            record = getRecordInternal(dataKey, false);
        } else {
            accessRecord(record);
        }
        final Object data = record != null ? record.getValue() : null;
        return new AbstractMap.SimpleImmutableEntry<Data, Object>(dataKey, data);
    }

    private Record getRecordInternal(Data key, boolean enableIndex) {
        Record record = null;
        final Object value = mapDataStore.load(key);
        if (value != null) {
            record = createRecord(key, value, getNow());
            records.put(key, record);
            if (enableIndex) {
                saveIndex(record);
            }
            updateSizeEstimator(calculateRecordHeapCost(record));
        }
        return record;
    }

    @Override
    public Set<Data> keySet() {
        checkIfLoaded();
        Set<Data> keySet = new HashSet<Data>(records.size());
        for (Record record : records.values()) {
            record = nullIfExpired(record);
            if (record == null) {
                continue;
            }
            keySet.add(record.getKey());
        }
        return keySet;
    }

    @Override
    public Collection<Data> valuesData() {
        checkIfLoaded();
        Collection<Data> values = new ArrayList<Data>(records.size());
        for (Record record : records.values()) {
            record = nullIfExpired(record);
            if (record == null) {
                continue;
            }
            values.add(toData(record.getValue()));
        }
        return values;
    }

    @Override
    public int clear() {
        checkIfLoaded();
        resetSizeEstimator();
        final Collection<Data> lockedKeys = lockStore != null ? lockStore.getLockedKeys() : Collections.<Data>emptySet();
        final Map<Data, Record> lockedRecords = new HashMap<Data, Record>(lockedKeys.size());
        // Locked records should not be removed!
        for (Data key : lockedKeys) {
            Record record = records.get(key);
            if (record != null) {
                lockedRecords.put(key, record);
                updateSizeEstimator(calculateRecordHeapCost(record));
            }
        }
        Set<Data> keysToDelete = records.keySet();
        keysToDelete.removeAll(lockedRecords.keySet());

        mapDataStore.removeAll(keysToDelete);

        int numOfClearedEntries = keysToDelete.size();
        removeIndex(keysToDelete);

        clearRecordsMap(lockedRecords);
        resetAccessSequenceNumber();
        mapDataStore.reset();
        return numOfClearedEntries;
    }

    @Override
    public void reset() {
        checkIfLoaded();

        clearRecordsMap(Collections.<Data, Record>emptyMap());
        resetSizeEstimator();
        resetAccessSequenceNumber();
        mapDataStore.reset();
    }


    @Override
    public Object evict(Data key) {
        checkIfLoaded();
        return evictInternal(key);
    }

    Object evictInternal(Data key) {
        Record record = records.get(key);
        Object value = null;
        if (record != null) {
            value = record.getValue();
            final long lastUpdateTime = record.getLastUpdateTime();
            mapDataStore.flush(key, value, lastUpdateTime);
            mapServiceContext.interceptRemove(name, value);
            updateSizeEstimator(-calculateRecordHeapCost(record));
            deleteRecord(key);
            removeIndex(key);
        }
        return value;
    }

    @Override
    public int evictAll() {
        checkIfLoaded();
        final int size = size();
        final Set<Data> keysToPreserve = evictAllInternal();
        removeIndexByPreservingKeys(keysToPreserve);
        return size - keysToPreserve.size();
    }

    @Override
    public void evictAllBackup() {
        evictAllInternal();
    }

    /**
     * Internal evict all provides common functionality to all {@link #evictAll()}
     *
     * @return preserved keys.
     */
    private Set<Data> evictAllInternal() {
        resetSizeEstimator();
        resetAccessSequenceNumber();

        Set<Data> keysToPreserve = Collections.emptySet();
        final Map<Data, Record> recordsToPreserve = getLockedRecords();
        if (!recordsToPreserve.isEmpty()) {
            keysToPreserve = recordsToPreserve.keySet();
            updateSizeEstimator(calculateRecordHeapCost(recordsToPreserve.values()));
        }
        flush(recordsToPreserve);
        clearRecordsMap(recordsToPreserve);
        return keysToPreserve;
    }

    /**
     * Flushes evicted records to map store.
     *
     * @param excludeRecords Records which should not be flushed.
     */
    private void flush(Map<Data, Record> excludeRecords) {
        Iterator<Record> iterator = records.values().iterator();
        while (iterator.hasNext()) {
            Record record = iterator.next();
            if (excludeRecords == null || !excludeRecords.containsKey(record.getKey())) {
                final Data key = record.getKey();
                final long lastUpdateTime = record.getLastUpdateTime();
                mapDataStore.flush(key, record.getValue(), lastUpdateTime);
            }
        }
    }

    /**
     * Removes indexes by excluding keysToPreserve.
     *
     * @param keysToPreserve should not be removed from index.
     */
    private void removeIndexByPreservingKeys(Set<Data> keysToPreserve) {
        final Set<Data> currentKeySet = records.keySet();
        currentKeySet.removeAll(keysToPreserve);

        removeIndex(currentKeySet);
    }

    /**
     * Returns locked records.
     *
     * @return map of locked records.
     */
    private Map<Data, Record> getLockedRecords() {
        if (lockStore == null) {
            return Collections.emptyMap();
        }
        final Collection<Data> lockedKeys = lockStore.getLockedKeys();
        if (lockedKeys.isEmpty()) {
            return Collections.emptyMap();
        }
        final Map<Data, Record> lockedRecords = new HashMap<Data, Record>(lockedKeys.size());
        // Locked records should not be removed!
        for (Data key : lockedKeys) {
            Record record = records.get(key);
            if (record != null) {
                lockedRecords.put(key, record);
            }
        }
        return lockedRecords;
    }

    @Override
    public void removeBackup(Data key) {
        final long now = getNow();
        earlyWriteCleanup(now);

        final Record record = records.get(key);
        if (record == null) {
            return;
        }
        // reduce size
        updateSizeEstimator(-calculateRecordHeapCost(record));
        deleteRecord(key);
        mapDataStore.removeBackup(key, now);
    }

    @Override
    public boolean remove(Data key, Object testValue) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);

        Record record = records.get(key);
        Object oldValue;
        boolean removed = false;
        if (record == null) {
            oldValue = mapDataStore.load(key);
            if (oldValue == null) {
                return false;
            }
        } else {
            oldValue = record.getValue();
        }
        if (mapServiceContext.compare(name, testValue, oldValue)) {
            mapServiceContext.interceptRemove(name, oldValue);
            removeIndex(key);
            mapDataStore.remove(key, now);
            onStore(record);
            // reduce size
            updateSizeEstimator(-calculateRecordHeapCost(record));
            deleteRecord(key);
            removed = true;
        }
        return removed;
    }

    @Override
    public Object remove(Data key) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);

        Record record = records.get(key);
        Object oldValue;
        if (record == null) {
            oldValue = mapDataStore.load(key);
            if (oldValue != null) {
                removeIndex(key);
                mapDataStore.remove(key, now);
            }
        } else {
            oldValue = record.getValue();
            oldValue = mapServiceContext.interceptRemove(name, oldValue);
            if (oldValue != null) {
                removeIndex(key);
                mapDataStore.remove(key, now);
                onStore(record);
            }
            // reduce size
            updateSizeEstimator(-calculateRecordHeapCost(record));
            deleteRecord(key);
        }
        return oldValue;
    }

    @Override
    public Object get(Data key) {
        checkIfLoaded();
        long now = getNow();
        final Object value = get0(key, now);
        postReadCleanUp(now);
        return value;
    }

    private Object get0(Data key, long now) {
        Record record = records.get(key);
        record = nullIfExpired(record);
        Object value;
        if (record == null) {
            value = mapDataStore.load(key);
            if (value != null) {
                record = createRecord(key, value, now);
                records.put(key, record);
                saveIndex(record);
                updateSizeEstimator(calculateRecordHeapCost(record));
            }
        } else {
            accessRecord(record, now);
            value = record.getValue();
        }
        value = mapServiceContext.interceptGet(name, value);
        return value;
    }


    @Override
    public MapEntrySet getAll(Set<Data> keys) {
        checkIfLoaded();
        final long now = getNow();

        final MapEntrySet mapEntrySet = new MapEntrySet();

        final Iterator<Data> iterator = keys.iterator();
        while (iterator.hasNext()) {
            final Data key = iterator.next();
            Record record = records.get(key);
            record = nullIfExpired(record);
            if (record != null) {
                addMapEntrySet(record.getKey(), record.getValue(), mapEntrySet);
                iterator.remove();
            }
        }
        addMapEntrySet(mapDataStore.loadAll(keys), mapEntrySet);

        postReadCleanUp(now);
        return mapEntrySet;
    }

    private void addMapEntrySet(Object key, Object value, MapEntrySet mapEntrySet) {
        if (key == null || value == null) {
            return;
        }
        final Data dataKey = mapServiceContext.toData(key);
        final Data dataValue = mapServiceContext.toData(value);
        mapEntrySet.add(dataKey, dataValue);
    }


    private void addMapEntrySet(Map<Object, Object> entries, MapEntrySet mapEntrySet) {
        for (Map.Entry<Object, Object> entry : entries.entrySet()) {
            addMapEntrySet(entry.getKey(), entry.getValue(), mapEntrySet);
        }
    }


    @Override
    public boolean containsKey(Data key) {
        checkIfLoaded();

        final long now = getNow();
        Record record = records.get(key);
        record = nullIfExpired(record);

        if (record == null) {
            Object value = mapDataStore.load(key);
            if (value != null) {
                record = createRecord(key, value, now);
                records.put(key, record);
                updateSizeEstimator(calculateRecordHeapCost(record));
            }
        }
        boolean contains = record != null;
        if (contains) {
            accessRecord(record, now);
        }
        postReadCleanUp(now);
        return contains;
    }

    @Override
    public void put(Map.Entry<Data, Object> entry) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);

        Data key = entry.getKey();
        Object value = entry.getValue();
        Record record = records.get(key);
        if (record == null) {
            value = mapServiceContext.interceptPut(name, null, value);
            value = mapDataStore.add(key, value, now);
            record = createRecord(key, value, now);
            records.put(key, record);
            // increase size.
            updateSizeEstimator(calculateRecordHeapCost(record));
            saveIndex(record);
        } else {
            final Object oldValue = record.getValue();
            value = mapServiceContext.interceptPut(name, oldValue, value);
            value = mapDataStore.add(key, value, now);
            onStore(record);
            // if key exists before, first reduce size
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, value, now);
            // then increase size
            updateSizeEstimator(calculateRecordHeapCost(record));
            saveIndex(record);
        }
    }

    @Override
    public Object put(Data key, Object value, long ttl) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);
        markRecordStoreExpirable(ttl);

        Record record = records.get(key);
        Object oldValue;
        if (record == null) {
            oldValue = mapDataStore.load(key);
            value = mapServiceContext.interceptPut(name, null, value);
            value = mapDataStore.add(key, value, now);
            record = createRecord(key, value, ttl, now);
            records.put(key, record);
            updateSizeEstimator(calculateRecordHeapCost(record));
            saveIndex(record);
        } else {
            oldValue = record.getValue();
            value = mapServiceContext.interceptPut(name, oldValue, value);
            value = mapDataStore.add(key, value, now);
            onStore(record);
            // if key exists before, first reduce size
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, value, now);
            // then increase size.
            updateSizeEstimator(calculateRecordHeapCost(record));
            updateTtl(record, ttl);
            saveIndex(record);
        }

        return oldValue;
    }

    @Override
    public boolean set(Data key, Object value, long ttl) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);
        markRecordStoreExpirable(ttl);

        Record record = records.get(key);
        boolean newRecord = false;
        if (record == null) {
            value = mapServiceContext.interceptPut(name, null, value);
            value = mapDataStore.add(key, value, now);
            record = createRecord(key, value, ttl, now);
            records.put(key, record);
            updateSizeEstimator(calculateRecordHeapCost(record));
            newRecord = true;
        } else {
            value = mapServiceContext.interceptPut(name, record.getValue(), value);
            value = mapDataStore.add(key, value, now);
            onStore(record);
            // if key exists before, first reduce size
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, value, now);
            // then increase size.
            updateSizeEstimator(calculateRecordHeapCost(record));
            updateTtl(record, ttl);
        }
        saveIndex(record);

        return newRecord;
    }

    @Override
    public boolean merge(Data key, EntryView mergingEntry, MapMergePolicy mergePolicy) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);
        Record record = records.get(key);
        Object newValue;
        if (record == null) {
            final Object notExistingKey = mapServiceContext.toObject(key);
            final EntryView nullEntryView = EntryViews.createNullEntryView(notExistingKey);
            newValue = mergePolicy.merge(name, mergingEntry, nullEntryView);
            if (newValue == null) {
                return false;
            }
            newValue = mapDataStore.add(key, newValue, now);
            record = createRecord(key, newValue, now);
            records.put(key, record);
            updateSizeEstimator(calculateRecordHeapCost(record));
        } else {
            Object oldValue = record.getValue();
            EntryView existingEntry = EntryViews.createLazyEntryView(record.getKey(), record.getValue(),
                    record, serializationService, mergePolicy);
            newValue = mergePolicy.merge(name, mergingEntry, existingEntry);
            // existing entry will be removed
            if (newValue == null) {
                removeIndex(key);
                mapDataStore.remove(key, now);
                onStore(record);
                // reduce size.
                updateSizeEstimator(-calculateRecordHeapCost(record));
                //remove from map & invalidate.
                deleteRecord(key);
                return true;
            }
            // same with the existing entry so no need to mapstore etc operations.
            if (mapServiceContext.compare(name, newValue, oldValue)) {
                return true;
            }
            newValue = mapDataStore.add(key, newValue, now);
            onStore(record);
            updateSizeEstimator(-calculateRecordHeapCost(record));
            recordFactory.setValue(record, newValue);
            updateSizeEstimator(calculateRecordHeapCost(record));
        }
        saveIndex(record);
        return newValue != null;
    }

    // TODO why does not replace method load data from map store if currently not available in memory.
    @Override
    public Object replace(Data key, Object value) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);

        Record record = records.get(key);
        Object oldValue;
        if (record != null && record.getValue() != null) {
            oldValue = record.getValue();
            value = mapServiceContext.interceptPut(name, oldValue, value);
            value = mapDataStore.add(key, value, now);
            onStore(record);
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, value, now);
            updateSizeEstimator(calculateRecordHeapCost(record));
        } else {
            return null;
        }
        saveIndex(record);

        return oldValue;
    }

    @Override
    public boolean replace(Data key, Object testValue, Object newValue) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);

        Record record = records.get(key);
        if (record == null) {
            return false;
        }
        if (mapServiceContext.compare(name, record.getValue(), testValue)) {
            newValue = mapServiceContext.interceptPut(name, record.getValue(), newValue);
            newValue = mapDataStore.add(key, newValue, now);
            onStore(record);
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, newValue, now);
            updateSizeEstimator(calculateRecordHeapCost(record));
        } else {
            return false;
        }
        saveIndex(record);

        return true;
    }

    @Override
    public void putTransient(Data key, Object value, long ttl) {
        checkIfLoaded();
        final long now = getNow();
        earlyWriteCleanup(now);
        markRecordStoreExpirable(ttl);

        Record record = records.get(key);
        if (record == null) {
            value = mapServiceContext.interceptPut(name, null, value);
            record = createRecord(key, value, ttl, now);
            records.put(key, record);
            updateSizeEstimator(calculateRecordHeapCost(record));
        } else {
            value = mapServiceContext.interceptPut(name, record.getValue(), value);
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, value, now);
            updateSizeEstimator(calculateRecordHeapCost(record));
            updateTtl(record, ttl);
        }
        saveIndex(record);
        mapDataStore.addTransient(key, now);
    }

    @Override
    public Object putFromLoad(Data key, Object value) {
        return putFromLoad(key, value, DEFAULT_TTL);
    }

    @Override
    public Object putFromLoad(Data key, Object value, long ttl) {
        final long now = getNow();
        earlyWriteCleanup(now);
        markRecordStoreExpirable(ttl);

        Record record = records.get(key);
        Object oldValue = null;
        if (record == null) {
            value = mapServiceContext.interceptPut(name, null, value);
            record = createRecord(key, value, ttl, now);
            records.put(key, record);
            updateSizeEstimator(calculateRecordHeapCost(record));
        } else {
            oldValue = record.getValue();
            value = mapServiceContext.interceptPut(name, record.getValue(), value);
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, value, now);
            updateSizeEstimator(calculateRecordHeapCost(record));
            updateTtl(record, ttl);
        }
        saveIndex(record);
        return oldValue;
    }

    @Override
    public boolean tryPut(Data key, Object value, long ttl) {
        checkIfLoaded();

        final long now = getNow();
        earlyWriteCleanup(now);
        markRecordStoreExpirable(ttl);

        Record record = records.get(key);
        if (record == null) {
            value = mapServiceContext.interceptPut(name, null, value);
            value = mapDataStore.add(key, value, now);
            record = createRecord(key, value, ttl, now);
            records.put(key, record);
            updateSizeEstimator(calculateRecordHeapCost(record));
        } else {
            value = mapServiceContext.interceptPut(name, record.getValue(), value);
            value = mapDataStore.add(key, value, now);
            onStore(record);
            updateSizeEstimator(-calculateRecordHeapCost(record));
            setRecordValue(record, value, now);
            updateSizeEstimator(calculateRecordHeapCost(record));
            updateTtl(record, ttl);
        }
        saveIndex(record);

        return true;
    }

    @Override
    public Object putIfAbsent(Data key, Object value, long ttl) {
        checkIfLoaded();

        final long now = getNow();
        earlyWriteCleanup(now);
        markRecordStoreExpirable(ttl);

        Record record = records.get(key);
        Object oldValue;
        if (record == null) {
            oldValue = mapDataStore.load(key);
            if (oldValue != null) {
                record = createRecord(key, oldValue, now);
                records.put(key, record);
                updateSizeEstimator(calculateRecordHeapCost(record));
            }
        } else {
            accessRecord(record, now);
            oldValue = record.getValue();
        }
        if (oldValue == null) {
            value = mapServiceContext.interceptPut(name, null, value);
            value = mapDataStore.add(key, value, now);
            onStore(record);
            record = createRecord(key, value, ttl, now);
            records.put(key, record);
            updateSizeEstimator(calculateRecordHeapCost(record));
            updateTtl(record, ttl);
        }
        saveIndex(record);

        return oldValue;
    }


    @Override
    public void loadAllFromStore(List<Data> keys, boolean replaceExistingValues) {
        if (keys.isEmpty()) {
            return;
        }
        recordStoreLoader.loadKeys(keys, replaceExistingValues);
    }

    @Override
    public MapDataStore<Data, Object> getMapDataStore() {
        return mapDataStore;
    }

}
TOP

Related Classes of com.hazelcast.map.DefaultRecordStore

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.