Package com.hazelcast.map.mapstore.writebehind

Source Code of com.hazelcast.map.mapstore.writebehind.StoreWorker

/*
* 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.mapstore.writebehind;

import com.hazelcast.cluster.ClusterService;
import com.hazelcast.map.MapContainer;
import com.hazelcast.map.MapServiceContext;
import com.hazelcast.map.PartitionContainer;
import com.hazelcast.map.RecordStore;
import com.hazelcast.nio.Address;
import com.hazelcast.partition.InternalPartition;
import com.hazelcast.partition.InternalPartitionService;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.util.Clock;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* Used to process store operations in another thread.
* Collects entries from write behind queues and passes them to {@link #writeBehindProcessor}.
* Created per map.
*/
public class StoreWorker implements Runnable {

    private final String mapName;

    private final MapServiceContext mapServiceContext;

    private final WriteBehindProcessor writeBehindProcessor;

    /**
     * Run on backup nodes after this interval.
     */
    private final long backupRunIntervalTime;

    /**
     * Last run time of this processor.
     */
    private long lastRunTime;


    public StoreWorker(MapContainer mapContainer, WriteBehindProcessor writeBehindProcessor) {
        this.mapName = mapContainer.getName();
        this.mapServiceContext = mapContainer.getMapServiceContext();
        this.writeBehindProcessor = writeBehindProcessor;
        this.backupRunIntervalTime = getReplicaWaitTime();
        this.lastRunTime = Clock.currentTimeMillis();
    }


    private long getReplicaWaitTime() {
        return TimeUnit.SECONDS.toMillis(mapServiceContext.getNodeEngine().getGroupProperties()
                .MAP_REPLICA_SCHEDULED_TASK_DELAY_SECONDS.getInteger());
    }

    @Override
    public void run() {
        final long now = Clock.currentTimeMillis();
        final MapServiceContext mapServiceContext = this.mapServiceContext;
        final NodeEngine nodeEngine = mapServiceContext.getNodeEngine();
        final ClusterService clusterService = nodeEngine.getClusterService();
        final InternalPartitionService partitionService = nodeEngine.getPartitionService();
        final Address thisAddress = clusterService.getThisAddress();
        final int partitionCount = partitionService.getPartitionCount();
        Map<Integer, Integer> partitionToEntryCountHolder = Collections.emptyMap();
        List<DelayedEntry> entries = Collections.emptyList();
        boolean createLazy = true;
        for (int partitionId = 0; partitionId < partitionCount; partitionId++) {
            final InternalPartition partition = partitionService.getPartition(partitionId, false);
            final Address owner = partition.getOwnerOrNull();
            final RecordStore recordStore = getRecordStoreOrNull(mapName, partitionId);
            if (owner == null || recordStore == null) {
                // no-op because no owner is set yet.
                // Therefore we don't know anything about the map
                continue;
            }
            final WriteBehindQueue<DelayedEntry> queue = getWriteBehindQueue(recordStore);
            final List<DelayedEntry> delayedEntries = filterItemsLessThanOrEqualToTime(queue, now);
            if (delayedEntries.isEmpty()) {
                continue;
            }
            if (!owner.equals(thisAddress)) {
                if (now < lastRunTime + backupRunIntervalTime) {
                    doInBackup(queue, delayedEntries, partitionId);
                }
                continue;
            }
            // initialize when needed, we do not want
            // to create these on backups for every second.
            if (createLazy) {
                partitionToEntryCountHolder = new HashMap<Integer, Integer>();
                entries = new ArrayList<DelayedEntry>();
                createLazy = false;
            }
            partitionToEntryCountHolder.put(partitionId, delayedEntries.size());
            entries.addAll(delayedEntries);
        }
        if (!entries.isEmpty()) {
            final Map<Integer, List<DelayedEntry>> failsPerPartition = writeBehindProcessor.process(entries);
            removeProcessed(mapName, getEntryPerPartitionMap(entries));
            addFailsToQueue(mapName, failsPerPartition);
            lastRunTime = now;
        }
    }

    private void removeProcessed(String mapName, Map<Integer, List<DelayedEntry>> entryListPerPartition) {
        for (Map.Entry<Integer, List<DelayedEntry>> entry : entryListPerPartition.entrySet()) {
            final int partitionId = entry.getKey();
            final RecordStore recordStore = getRecordStoreOrNull(mapName, partitionId);
            if (recordStore == null) {
                continue;
            }
            final WriteBehindQueue<DelayedEntry> queue = getWriteBehindQueue(recordStore);
            final List<DelayedEntry> entries = entry.getValue();
            queue.removeAll(entries);
        }
    }


    private Map<Integer, List<DelayedEntry>> getEntryPerPartitionMap(List<DelayedEntry> entries) {
        final Map<Integer, List<DelayedEntry>> entryListPerPartition = new HashMap<Integer, List<DelayedEntry>>();
        for (DelayedEntry entry : entries) {
            final int partitionId = entry.getPartitionId();
            List<DelayedEntry> delayedEntries = entryListPerPartition.get(partitionId);
            if (delayedEntries == null) {
                delayedEntries = new ArrayList<DelayedEntry>();
                entryListPerPartition.put(partitionId, delayedEntries);
            }
            delayedEntries.add(entry);
        }
        return entryListPerPartition;
    }

    /**
     * @param queue          write behind queue.
     * @param delayedEntries entries to be processed.
     * @param partitionId    corresponding partition id.
     */
    private void doInBackup(final WriteBehindQueue queue, final List<DelayedEntry> delayedEntries, final int partitionId) {
        final NodeEngine nodeEngine = mapServiceContext.getNodeEngine();
        final ClusterService clusterService = nodeEngine.getClusterService();
        final InternalPartitionService partitionService = nodeEngine.getPartitionService();
        final Address thisAddress = clusterService.getThisAddress();
        final InternalPartition partition = partitionService.getPartition(partitionId, false);
        final Address owner = partition.getOwnerOrNull();
        if (owner != null && !owner.equals(thisAddress)) {
            writeBehindProcessor.callBeforeStoreListeners(delayedEntries);
            removeProcessed(mapName, getEntryPerPartitionMap(delayedEntries));
            writeBehindProcessor.callAfterStoreListeners(delayedEntries);
        }
    }

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

    private void addFailsToQueue(String mapName, Map<Integer, List<DelayedEntry>> failsPerPartition) {
        if (failsPerPartition.isEmpty()) {
            return;
        }
        for (Map.Entry<Integer, List<DelayedEntry>> entry : failsPerPartition.entrySet()) {
            final Integer partitionId = entry.getKey();
            final List<DelayedEntry> fails = failsPerPartition.get(partitionId);
            if (fails == null || fails.isEmpty()) {
                continue;
            }
            final RecordStore recordStore = getRecordStoreOrNull(mapName, partitionId);
            if (recordStore == null) {
                continue;
            }
            final WriteBehindQueue<DelayedEntry> queue = getWriteBehindQueue(recordStore);
            queue.addFront(fails);
        }
    }

    private static List<DelayedEntry> filterItemsLessThanOrEqualToTime(WriteBehindQueue<DelayedEntry> queue,
                                                                       long now) {
        if (queue == null || queue.size() == 0) {
            return Collections.emptyList();
        }

        return queue.filterItems(now);
    }

    private WriteBehindQueue<DelayedEntry> getWriteBehindQueue(RecordStore recordStore) {
        final WriteBehindStore storeManager = (WriteBehindStore) recordStore.getMapDataStore();
        return storeManager.getWriteBehindQueue();
    }
}
TOP

Related Classes of com.hazelcast.map.mapstore.writebehind.StoreWorker

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.