/*
* 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.operation;
import com.hazelcast.map.impl.MapDataSerializerHook;
import com.hazelcast.map.impl.MapService;
import com.hazelcast.map.impl.querycache.QueryCacheContext;
import com.hazelcast.map.impl.querycache.QueryCacheEventService;
import com.hazelcast.map.impl.querycache.accumulator.Accumulator;
import com.hazelcast.map.impl.querycache.accumulator.AccumulatorHandler;
import com.hazelcast.map.impl.querycache.accumulator.AccumulatorInfo;
import com.hazelcast.map.impl.querycache.event.sequence.Sequenced;
import com.hazelcast.map.impl.querycache.publisher.EventPublisherAccumulatorProcessor;
import com.hazelcast.map.impl.querycache.publisher.MapPublisherRegistry;
import com.hazelcast.map.impl.querycache.publisher.PartitionAccumulatorRegistry;
import com.hazelcast.map.impl.querycache.publisher.PublisherAccumulatorHandler;
import com.hazelcast.map.impl.querycache.publisher.PublisherContext;
import com.hazelcast.map.impl.querycache.publisher.PublisherRegistry;
import com.hazelcast.nio.serialization.IdentifiedDataSerializable;
import com.hazelcast.spi.NodeEngine;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.PartitionAwareOperation;
import com.hazelcast.spi.partition.IPartition;
import com.hazelcast.spi.partition.IPartitionService;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import static com.hazelcast.util.Preconditions.checkPositive;
/**
* Processes remaining items in {@link Accumulator} instances on a partition.
* If the partition is not owned by this node, {@link Accumulator} will be removed.
*/
public class AccumulatorConsumerOperation extends Operation implements PartitionAwareOperation, IdentifiedDataSerializable {
private int maxProcessableAccumulatorCount;
private Queue<Accumulator> accumulators;
public AccumulatorConsumerOperation() {
}
public AccumulatorConsumerOperation(Queue<Accumulator> accumulators, int maxProcessableAccumulatorCount) {
checkPositive(maxProcessableAccumulatorCount, "maxProcessableAccumulatorCount");
this.accumulators = accumulators;
this.maxProcessableAccumulatorCount = maxProcessableAccumulatorCount;
}
@Override
public void run() throws Exception {
QueryCacheContext context = getQueryCacheContext();
QueryCacheEventService queryCacheEventService = context.getQueryCacheEventService();
EventPublisherAccumulatorProcessor processor = new EventPublisherAccumulatorProcessor(queryCacheEventService);
AccumulatorHandler<Sequenced> handler = new PublisherAccumulatorHandler(context, processor);
int processed = 0;
do {
Accumulator accumulator = accumulators.poll();
if (accumulator == null) {
break;
}
if (isLocal()) {
// consume the accumulator if only this node is the owner
// of accumulators partition
publishAccumulator(processor, handler, accumulator);
} else {
// if the accumulator is not local, it should be a leftover
// stayed after partition migrations and remove that accumulator
removeAccumulator(context, accumulator);
}
processed++;
} while (processed <= maxProcessableAccumulatorCount);
}
@Override
public boolean returnsResponse() {
return false;
}
private void publishAccumulator(EventPublisherAccumulatorProcessor processor,
AccumulatorHandler<Sequenced> handler, Accumulator accumulator) {
AccumulatorInfo info = accumulator.getInfo();
processor.setInfo(info);
accumulator.poll(handler, info.getDelaySeconds(), TimeUnit.SECONDS);
}
private QueryCacheContext getQueryCacheContext() {
MapService mapService = getService();
return mapService.getMapServiceContext().getQueryCacheContext();
}
private boolean isLocal() {
NodeEngine nodeEngine = getNodeEngine();
IPartitionService partitionService = nodeEngine.getPartitionService();
IPartition partition = partitionService.getPartition(getPartitionId());
return partition.isLocal();
}
private void removeAccumulator(QueryCacheContext context, Accumulator accumulator) {
PublisherContext publisherContext = context.getPublisherContext();
MapPublisherRegistry mapPublisherRegistry = publisherContext.getMapPublisherRegistry();
AccumulatorInfo info = accumulator.getInfo();
String mapName = info.getMapName();
String cacheName = info.getCacheName();
PublisherRegistry publisherRegistry = mapPublisherRegistry.getOrNull(mapName);
if (publisherRegistry == null) {
return;
}
PartitionAccumulatorRegistry partitionAccumulatorRegistry = publisherRegistry.getOrNull(cacheName);
if (partitionAccumulatorRegistry == null) {
return;
}
partitionAccumulatorRegistry.remove(getPartitionId());
}
@Override
public int getFactoryId() {
return MapDataSerializerHook.F_ID;
}
@Override
public int getId() {
return MapDataSerializerHook.ACCUMULATOR_CONSUMER;
}
}