All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.spi.impl.operationservice.impl.OperationServiceImpl Maven / Gradle / Ivy

There is a newer version: 3.9.3
Show newest version
/*
 * Copyright (c) 2008-2016, 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.spi.impl.operationservice.impl;

import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.instance.MemberImpl;
import com.hazelcast.instance.Node;
import com.hazelcast.internal.cluster.ClusterClock;
import com.hazelcast.internal.management.dto.SlowOperationDTO;
import com.hazelcast.internal.metrics.MetricsProvider;
import com.hazelcast.internal.metrics.MetricsRegistry;
import com.hazelcast.internal.metrics.Probe;
import com.hazelcast.internal.partition.InternalPartitionService;
import com.hazelcast.internal.serialization.InternalSerializationService;
import com.hazelcast.internal.util.counters.MwCounter;
import com.hazelcast.logging.ILogger;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.Connection;
import com.hazelcast.nio.ConnectionManager;
import com.hazelcast.nio.Packet;
import com.hazelcast.spi.ExecutionService;
import com.hazelcast.spi.InternalCompletableFuture;
import com.hazelcast.spi.InvocationBuilder;
import com.hazelcast.spi.LiveOperations;
import com.hazelcast.spi.LiveOperationsTracker;
import com.hazelcast.spi.Operation;
import com.hazelcast.spi.OperationFactory;
import com.hazelcast.spi.OperationService;
import com.hazelcast.spi.impl.NodeEngineImpl;
import com.hazelcast.spi.impl.PacketHandler;
import com.hazelcast.spi.impl.PartitionSpecificRunnable;
import com.hazelcast.spi.impl.operationexecutor.OperationExecutor;
import com.hazelcast.spi.impl.operationexecutor.impl.OperationExecutorImpl;
import com.hazelcast.spi.impl.operationexecutor.slowoperationdetector.SlowOperationDetector;
import com.hazelcast.spi.impl.operationservice.InternalOperationService;
import com.hazelcast.spi.impl.operationservice.impl.responses.Response;
import com.hazelcast.util.EmptyStatement;
import com.hazelcast.util.executor.ExecutorType;
import com.hazelcast.util.executor.ManagedExecutorService;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import static com.hazelcast.internal.metrics.ProbeLevel.MANDATORY;
import static com.hazelcast.nio.Packet.FLAG_OP;
import static com.hazelcast.nio.Packet.FLAG_RESPONSE;
import static com.hazelcast.nio.Packet.FLAG_URGENT;
import static com.hazelcast.spi.InvocationBuilder.DEFAULT_CALL_TIMEOUT;
import static com.hazelcast.spi.InvocationBuilder.DEFAULT_DESERIALIZE_RESULT;
import static com.hazelcast.spi.InvocationBuilder.DEFAULT_REPLICA_INDEX;
import static com.hazelcast.spi.InvocationBuilder.DEFAULT_TRY_COUNT;
import static com.hazelcast.spi.InvocationBuilder.DEFAULT_TRY_PAUSE_MILLIS;
import static com.hazelcast.spi.impl.operationutil.Operations.isJoinOperation;
import static com.hazelcast.spi.properties.GroupProperty.OPERATION_CALL_TIMEOUT_MILLIS;
import static com.hazelcast.util.Preconditions.checkNotNegative;
import static com.hazelcast.util.Preconditions.checkNotNull;

/**
 * This is the implementation of the {@link com.hazelcast.spi.impl.operationservice.InternalOperationService}.
 * 

*

System Operation

* When a {@link com.hazelcast.spi.UrgentSystemOperation} is invoked on this OperationService, it will be executed with a * high urgency by making use of a urgent queue. So when the system is under load, and the operation queues are * filled, then system operations are executed before normal operation. The advantage is that when a system is under * pressure, it still is able to do things like recognizing new members in the cluster and moving partitions around. *

* When a UrgentSystemOperation is send to a remote machine, it is wrapped in a {@link Packet} and the packet is marked as a * urgent packet. When this packet is received on the remove OperationService, the urgent flag is checked and if * needed, the operation is set on the urgent queue. So local and remote execution of System operations will obey * the urgency. * * @see Invocation * @see InvocationBuilderImpl * @see PartitionInvocation * @see TargetInvocation */ public final class OperationServiceImpl implements InternalOperationService, MetricsProvider, LiveOperationsTracker { private static final int CORE_SIZE_CHECK = 8; private static final int CORE_SIZE_FACTOR = 4; private static final int CONCURRENCY_LEVEL = 16; private static final int ASYNC_QUEUE_CAPACITY = 100000; private static final long TERMINATION_TIMEOUT_MILLIS = TimeUnit.SECONDS.toMillis(10); final InvocationRegistry invocationRegistry; final OperationExecutor operationExecutor; @Probe(name = "completedCount", level = MANDATORY) final AtomicLong completedOperationsCount = new AtomicLong(); @Probe(name = "operationTimeoutCount", level = MANDATORY) final MwCounter operationTimeoutCount = MwCounter.newMwCounter(); @Probe(name = "callTimeoutCount", level = MANDATORY) final MwCounter callTimeoutCount = MwCounter.newMwCounter(); @Probe(name = "retryCount", level = MANDATORY) final MwCounter retryCount = MwCounter.newMwCounter(); final NodeEngineImpl nodeEngine; final Node node; final ILogger logger; final OperationBackupHandler operationBackupHandler; final BackpressureRegulator backpressureRegulator; volatile Invocation.Context invocationContext; private final InvocationMonitor invocationMonitor; private final SlowOperationDetector slowOperationDetector; private final AsyncResponseHandler asyncResponseHandler; private final InternalSerializationService serializationService; private final ResponseHandler responseHandler; private final Address thisAddress; public OperationServiceImpl(NodeEngineImpl nodeEngine) { this.nodeEngine = nodeEngine; this.node = nodeEngine.getNode(); this.thisAddress = node.getThisAddress(); this.logger = node.getLogger(OperationService.class); this.serializationService = (InternalSerializationService) nodeEngine.getSerializationService(); this.backpressureRegulator = new BackpressureRegulator(node.getProperties(), logger); int coreSize = Runtime.getRuntime().availableProcessors(); boolean reallyMultiCore = coreSize >= CORE_SIZE_CHECK; int concurrencyLevel = reallyMultiCore ? coreSize * CORE_SIZE_FACTOR : CONCURRENCY_LEVEL; this.invocationRegistry = new InvocationRegistry(logger, backpressureRegulator.newCallIdSequence(), concurrencyLevel); this.invocationMonitor = new InvocationMonitor( nodeEngine, thisAddress, node.getHazelcastThreadGroup(), node.getProperties(), invocationRegistry, logger, (InternalSerializationService) nodeEngine.getSerializationService(), nodeEngine.getServiceManager()); this.operationBackupHandler = new OperationBackupHandler(this); this.responseHandler = new ResponseHandler(logger, node.getSerializationService(), invocationRegistry, nodeEngine); this.asyncResponseHandler = new AsyncResponseHandler( node.getHazelcastThreadGroup(), logger, responseHandler, node.getProperties()); this.operationExecutor = new OperationExecutorImpl( node.getProperties(), node.loggingService, thisAddress, new OperationRunnerFactoryImpl(this), node.getHazelcastThreadGroup(), node.getNodeExtension()); this.slowOperationDetector = new SlowOperationDetector(node.loggingService, operationExecutor.getGenericOperationRunners(), operationExecutor.getPartitionOperationRunners(), node.getProperties(), node.getHazelcastThreadGroup()); } @Override public void populate(LiveOperations result) { operationExecutor.scan(result); } public PacketHandler getAsyncResponseHandler() { return asyncResponseHandler; } public InvocationMonitor getInvocationMonitor() { return invocationMonitor; } @Override public List getSlowOperationDTOs() { return slowOperationDetector.getSlowOperationDTOs(); } public InvocationRegistry getInvocationRegistry() { return invocationRegistry; } public ResponseHandler getResponseHandler() { return responseHandler; } @Override public int getPartitionThreadCount() { return operationExecutor.getPartitionThreadCount(); } @Override public int getGenericThreadCount() { return operationExecutor.getGenericThreadCount(); } @Override public int getRunningOperationsCount() { return operationExecutor.getRunningOperationCount(); } @Override public long getExecutedOperationCount() { return completedOperationsCount.get(); } @Override public int getRemoteOperationsCount() { return invocationRegistry.size(); } @Override public int getOperationExecutorQueueSize() { return operationExecutor.getQueueSize(); } @Override public int getPriorityOperationExecutorQueueSize() { return operationExecutor.getPriorityQueueSize(); } public OperationExecutor getOperationExecutor() { return operationExecutor; } @Override public int getResponseQueueSize() { return asyncResponseHandler.getQueueSize(); } @Override public void execute(PartitionSpecificRunnable task) { operationExecutor.execute(task); } @Override public InvocationBuilder createInvocationBuilder(String serviceName, Operation op, int partitionId) { checkNotNegative(partitionId, "Partition id cannot be negative!"); return new InvocationBuilderImpl(invocationContext, serviceName, op, partitionId); } @Override public InvocationBuilder createInvocationBuilder(String serviceName, Operation op, Address target) { checkNotNull(target, "Target cannot be null!"); return new InvocationBuilderImpl(invocationContext, serviceName, op, target); } @Override public void runOperationOnCallingThread(Operation op) { operationExecutor.run(op); } @Override public void executeOperation(Operation op) { operationExecutor.execute(op); } @Override public boolean isRunAllowed(Operation op) { return operationExecutor.isRunAllowed(op); } @Override @SuppressWarnings("unchecked") public InternalCompletableFuture invokeOnPartition(String serviceName, Operation op, int partitionId) { op.setServiceName(serviceName) .setPartitionId(partitionId) .setReplicaIndex(DEFAULT_REPLICA_INDEX); return new PartitionInvocation( invocationContext, op, DEFAULT_TRY_COUNT, DEFAULT_TRY_PAUSE_MILLIS, DEFAULT_CALL_TIMEOUT, DEFAULT_DESERIALIZE_RESULT).invoke(); } @Override @SuppressWarnings("unchecked") public InternalCompletableFuture invokeOnPartition(Operation op) { return new PartitionInvocation( invocationContext, op, DEFAULT_TRY_COUNT, DEFAULT_TRY_PAUSE_MILLIS, DEFAULT_CALL_TIMEOUT, DEFAULT_DESERIALIZE_RESULT).invoke(); } @Override @SuppressWarnings("unchecked") public InternalCompletableFuture invokeOnTarget(String serviceName, Operation op, Address target) { op.setServiceName(serviceName); return new TargetInvocation(invocationContext, op, target, DEFAULT_TRY_COUNT, DEFAULT_TRY_PAUSE_MILLIS, DEFAULT_CALL_TIMEOUT, DEFAULT_DESERIALIZE_RESULT).invoke(); } @Override @SuppressWarnings("unchecked") public void asyncInvokeOnPartition(String serviceName, Operation op, int partitionId, ExecutionCallback callback) { op.setServiceName(serviceName).setPartitionId(partitionId).setReplicaIndex(DEFAULT_REPLICA_INDEX); InvocationFuture future = new PartitionInvocation(invocationContext, op, DEFAULT_TRY_COUNT, DEFAULT_TRY_PAUSE_MILLIS, DEFAULT_CALL_TIMEOUT, DEFAULT_DESERIALIZE_RESULT).invokeAsync(); if (callback != null) { future.andThen(callback); } } // =============================== processing operation =============================== @Override public boolean isCallTimedOut(Operation op) { // Join operations should not be checked for timeout // because caller is not member of this cluster // and can have a different clock. if (!op.returnsResponse() || isJoinOperation(op)) { return false; } long callTimeout = op.getCallTimeout(); long invocationTime = op.getInvocationTime(); long expireTime = invocationTime + callTimeout; if (expireTime <= 0 || expireTime == Long.MAX_VALUE) { return false; } ClusterClock clusterClock = nodeEngine.getClusterService().getClusterClock(); long now = clusterClock.getClusterTime(); if (expireTime < now) { return true; } return false; } @Override public Map invokeOnAllPartitions(String serviceName, OperationFactory operationFactory) throws Exception { Map> memberPartitions = nodeEngine.getPartitionService().getMemberPartitionsMap(); InvokeOnPartitions invokeOnPartitions = new InvokeOnPartitions(this, serviceName, operationFactory, memberPartitions); return invokeOnPartitions.invoke(); } @Override public Map invokeOnPartitions(String serviceName, OperationFactory operationFactory, Collection partitions) throws Exception { Map> memberPartitions = new HashMap>(3); InternalPartitionService partitionService = nodeEngine.getPartitionService(); for (int partition : partitions) { Address owner = partitionService.getPartitionOwnerOrWait(partition); if (!memberPartitions.containsKey(owner)) { memberPartitions.put(owner, new ArrayList()); } memberPartitions.get(owner).add(partition); } InvokeOnPartitions invokeOnPartitions = new InvokeOnPartitions(this, serviceName, operationFactory, memberPartitions); return invokeOnPartitions.invoke(); } @Override public boolean send(Operation op, Address target) { checkNotNull(target, "Target is required!"); if (thisAddress.equals(target)) { throw new IllegalArgumentException("Target is this node! -> " + target + ", op: " + op); } byte[] bytes = serializationService.toBytes(op); int partitionId = op.getPartitionId(); Packet packet = new Packet(bytes, partitionId) .setFlag(FLAG_OP); if (op.isUrgent()) { packet.setFlag(FLAG_URGENT); } ConnectionManager connectionManager = node.getConnectionManager(); Connection connection = connectionManager.getOrConnect(target); return connectionManager.transmit(packet, connection); } public boolean send(Response response, Address target) { checkNotNull(target, "Target is required!"); if (thisAddress.equals(target)) { throw new IllegalArgumentException("Target is this node! -> " + target + ", response: " + response); } byte[] bytes = serializationService.toBytes(response); Packet packet = new Packet(bytes, -1) .setAllFlags(FLAG_OP | FLAG_RESPONSE); if (response.isUrgent()) { packet.setFlag(FLAG_URGENT); } ConnectionManager connectionManager = node.getConnectionManager(); Connection connection = connectionManager.getOrConnect(target); return connectionManager.transmit(packet, connection); } public void onMemberLeft(MemberImpl member) { invocationMonitor.onMemberLeft(member); } public void reset() { invocationRegistry.reset(); } @Override public void provideMetrics(MetricsRegistry metricsRegistry) { metricsRegistry.scanAndRegister(this, "operation"); metricsRegistry.collectMetrics(invocationRegistry, invocationMonitor, responseHandler, asyncResponseHandler, operationExecutor); } public void start() { logger.finest("Starting OperationService"); ManagedExecutorService asyncExecutor = nodeEngine.getExecutionService().register( ExecutionService.ASYNC_EXECUTOR, Runtime.getRuntime().availableProcessors(), ASYNC_QUEUE_CAPACITY, ExecutorType.CONCRETE); this.invocationContext = new Invocation.Context( asyncExecutor, nodeEngine.getClusterService().getClusterClock(), nodeEngine.getClusterService(), node.connectionManager, node.nodeEngine.getExecutionService(), nodeEngine.getProperties().getMillis(OPERATION_CALL_TIMEOUT_MILLIS), invocationRegistry, invocationMonitor, nodeEngine.getLocalMember().getUuid(), nodeEngine.getLogger(Invocation.class), node, nodeEngine, nodeEngine.getPartitionService(), this, operationExecutor, retryCount, serializationService, nodeEngine.getThisAddress()); invocationMonitor.start(); operationExecutor.start(); asyncResponseHandler.start(); slowOperationDetector.start(); } public void shutdown() { logger.finest("Shutting down OperationService"); invocationRegistry.shutdown(); invocationMonitor.shutdown(); operationExecutor.shutdown(); asyncResponseHandler.shutdown(); slowOperationDetector.shutdown(); try { invocationMonitor.awaitTermination(TERMINATION_TIMEOUT_MILLIS); } catch (InterruptedException e) { //restore the interrupt. //todo: we need a better mechanism for dealing with interruption and waiting for termination Thread.currentThread().interrupt(); EmptyStatement.ignore(e); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy