/* * Copyright (c) 2008-2017, 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.querycache.accumulator; import com.hazelcast.map.impl.MapService; import com.hazelcast.map.impl.operation.AccumulatorConsumerOperation; import com.hazelcast.map.impl.querycache.QueryCacheContext; import com.hazelcast.map.impl.querycache.publisher.MapPublisherRegistry; import com.hazelcast.map.impl.querycache.publisher.PartitionAccumulatorRegistry; import com.hazelcast.map.impl.querycache.publisher.PublisherContext; import com.hazelcast.map.impl.querycache.publisher.PublisherRegistry; import com.hazelcast.spi.Operation; import com.hazelcast.spi.impl.NodeEngineImpl; import java.util.ArrayDeque; import java.util.HashMap; import java.util.Map; import java.util.Queue; import java.util.Set; /** * Task for scanning all {@link Accumulator} instances of all {@link com.hazelcast.map.QueryCache}s on this node. * <p/> * If it finds any event that needs to be published in an accumulator, it creates and sends * {@link AccumulatorConsumerOperation} to relevant partitions. * * @see AccumulatorConsumerOperation */ public class AccumulatorScannerTask implements Runnable { private static final int MAX_PROCESSABLE_ACCUMULATOR_COUNT = 10; private final ScannerConsumer consumer; private final QueryCacheContext context; public AccumulatorScannerTask(QueryCacheContext context) { this.context = context; this.consumer = new ScannerConsumer(); } @Override public void run() { scanAccumulators(); } void scanAccumulators() { PublisherContext publisherContext = context.getPublisherContext(); MapPublisherRegistry mapPublisherRegistry = publisherContext.getMapPublisherRegistry(); Map<String, PublisherRegistry> publisherRegistryMap = mapPublisherRegistry.getAll(); Set<Map.Entry<String, PublisherRegistry>> publishers = publisherRegistryMap.entrySet(); for (Map.Entry<String, PublisherRegistry> entry : publishers) { PublisherRegistry publisherRegistry = entry.getValue(); Map<String, PartitionAccumulatorRegistry> accumulatorRegistryMap = publisherRegistry.getAll(); Set<Map.Entry<String, PartitionAccumulatorRegistry>> accumulators = accumulatorRegistryMap.entrySet(); for (Map.Entry<String, PartitionAccumulatorRegistry> accumulatorRegistryEntry : accumulators) { PartitionAccumulatorRegistry accumulatorRegistry = accumulatorRegistryEntry.getValue(); Map<Integer, Accumulator> accumulatorMap = accumulatorRegistry.getAll(); for (Map.Entry<Integer, Accumulator> accumulatorEntry : accumulatorMap.entrySet()) { Integer partitionId = accumulatorEntry.getKey(); Accumulator accumulator = accumulatorEntry.getValue(); int size = accumulator.size(); if (size > 0) { consumer.consume(accumulator, partitionId); } } } } sendConsumerOperation(); consumer.reset(); } private void sendConsumerOperation() { Map<Integer, Queue<Accumulator>> partitionAccumulators = consumer.getPartitionAccumulators(); if (partitionAccumulators == null || partitionAccumulators.isEmpty()) { return; } Set<Map.Entry<Integer, Queue<Accumulator>>> entries = partitionAccumulators.entrySet(); for (Map.Entry<Integer, Queue<Accumulator>> entry : entries) { Integer partitionId = entry.getKey(); Queue<Accumulator> accumulators = entry.getValue(); if (accumulators.isEmpty()) { continue; } Operation operation = createConsumerOperation(partitionId, accumulators); context.getInvokerWrapper().executeOperation(operation); } } private Operation createConsumerOperation(int partitionId, Queue<Accumulator> accumulators) { PublisherContext publisherContext = context.getPublisherContext(); NodeEngineImpl nodeEngine = (NodeEngineImpl) publisherContext.getNodeEngine(); Operation operation = new AccumulatorConsumerOperation(accumulators, MAX_PROCESSABLE_ACCUMULATOR_COUNT); operation .setNodeEngine(nodeEngine) .setCallerUuid(nodeEngine.getLocalMember().getUuid()) .setPartitionId(partitionId) .setValidateTarget(false) .setService(nodeEngine.getService(MapService.SERVICE_NAME)); return operation; } /** * Handles collected accumulators in scanning phase. */ private static class ScannerConsumer { private Map<Integer, Queue<Accumulator>> partitionAccumulators; public ScannerConsumer() { } void consume(Accumulator accumulator, int partitionId) { if (partitionAccumulators == null) { partitionAccumulators = new HashMap<Integer, Queue<Accumulator>>(); } Queue<Accumulator> accumulators = partitionAccumulators.get(partitionId); if (accumulators == null) { accumulators = new ArrayDeque<Accumulator>(); partitionAccumulators.put(partitionId, accumulators); } accumulators.add(accumulator); } public Map<Integer, Queue<Accumulator>> getPartitionAccumulators() { return partitionAccumulators; } void reset() { if (partitionAccumulators != null) { partitionAccumulators.clear(); } } } }