@Override public void registerEndpoint(ClientEndpoint endpoint) { checkNotNull(endpoint, "endpoint can't be null"); final Connection conn = endpoint.getConnection(); if (endpoints.putIfAbsent(conn, endpoint) != null) { logger.severe("An endpoint already exists for connection:" + conn); } else { totalRegistrations.inc(); } }
/** * This is the implementation of the {@link * com.hazelcast.spi.impl.operationservice.InternalOperationService}. * * <p> * * <h1>System Operation</h1> * * 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. * * <p>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(), node.getLogger(BackpressureRegulator.class)); int coreSize = Runtime.getRuntime().availableProcessors(); boolean reallyMultiCore = coreSize >= CORE_SIZE_CHECK; int concurrencyLevel = reallyMultiCore ? coreSize * CORE_SIZE_FACTOR : CONCURRENCY_LEVEL; this.invocationRegistry = new InvocationRegistry( node.getLogger(OperationServiceImpl.class), backpressureRegulator.newCallIdSequence(), concurrencyLevel); this.invocationMonitor = new InvocationMonitor( nodeEngine, thisAddress, node.getHazelcastThreadGroup(), node.getProperties(), invocationRegistry, node.getLogger(InvocationMonitor.class), serializationService, nodeEngine.getServiceManager()); this.operationBackupHandler = new OperationBackupHandler(this); this.responseHandler = new ResponseHandler( node.getLogger(ResponseHandler.class), node.getSerializationService(), invocationRegistry, nodeEngine); this.asyncResponseHandler = new AsyncResponseHandler( node.getHazelcastThreadGroup(), node.getLogger(AsyncResponseHandler.class), 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<SlowOperationDTO> 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) { run(op); } @Override public void run(Operation op) { operationExecutor.run(op); } @Override public void executeOperation(Operation op) { execute(op); } @Override public void execute(Operation op) { operationExecutor.execute(op); } @Override public boolean isRunAllowed(Operation op) { return operationExecutor.isRunAllowed(op); } @Override @SuppressWarnings("unchecked") public <E> InternalCompletableFuture<E> 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 <E> InternalCompletableFuture<E> 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 <E> InternalCompletableFuture<E> 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 <V> void asyncInvokeOnPartition( String serviceName, Operation op, int partitionId, ExecutionCallback<V> 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<Integer, Object> invokeOnAllPartitions( String serviceName, OperationFactory operationFactory) throws Exception { Map<Address, List<Integer>> memberPartitions = nodeEngine.getPartitionService().getMemberPartitionsMap(); InvokeOnPartitions invokeOnPartitions = new InvokeOnPartitions(this, serviceName, operationFactory, memberPartitions); return invokeOnPartitions.invoke(); } @Override public Map<Integer, Object> invokeOnPartitions( String serviceName, OperationFactory operationFactory, Collection<Integer> partitions) throws Exception { Map<Address, List<Integer>> memberPartitions = new HashMap<Address, List<Integer>>(3); InternalPartitionService partitionService = nodeEngine.getPartitionService(); for (int partition : partitions) { Address owner = partitionService.getPartitionOwnerOrWait(partition); if (!memberPartitions.containsKey(owner)) { memberPartitions.put(owner, new ArrayList<Integer>()); } memberPartitions.get(owner).add(partition); } InvokeOnPartitions invokeOnPartitions = new InvokeOnPartitions(this, serviceName, operationFactory, memberPartitions); return invokeOnPartitions.invoke(); } @Override public Map<Integer, Object> invokeOnPartitions( String serviceName, OperationFactory operationFactory, int[] partitions) throws Exception { Map<Address, List<Integer>> memberPartitions = new HashMap<Address, List<Integer>>(3); InternalPartitionService partitionService = nodeEngine.getPartitionService(); for (int partition : partitions) { Address owner = partitionService.getPartitionOwnerOrWait(partition); if (!memberPartitions.containsKey(owner)) { memberPartitions.put(owner, new ArrayList<Integer>()); } 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); } } }