/* * 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.spi.impl.operationservice.impl; import com.hazelcast.internal.metrics.MetricsProvider; import com.hazelcast.internal.metrics.MetricsRegistry; import com.hazelcast.internal.metrics.Probe; import com.hazelcast.internal.partition.ReplicaErrorLogger; import com.hazelcast.internal.serialization.InternalSerializationService; import com.hazelcast.internal.util.counters.MwCounter; import com.hazelcast.internal.util.counters.SwCounter; import com.hazelcast.logging.ILogger; import com.hazelcast.nio.Address; import com.hazelcast.nio.Bits; import com.hazelcast.nio.Packet; import com.hazelcast.spi.impl.NodeEngineImpl; import com.hazelcast.spi.impl.PacketHandler; import com.hazelcast.spi.impl.operationservice.impl.responses.ErrorResponse; import java.nio.ByteOrder; import static com.hazelcast.internal.metrics.ProbeLevel.MANDATORY; import static com.hazelcast.internal.util.counters.MwCounter.newMwCounter; import static com.hazelcast.internal.util.counters.SwCounter.newSwCounter; import static com.hazelcast.spi.impl.SpiDataSerializerHook.BACKUP_ACK_RESPONSE; import static com.hazelcast.spi.impl.SpiDataSerializerHook.CALL_TIMEOUT_RESPONSE; import static com.hazelcast.spi.impl.SpiDataSerializerHook.ERROR_RESPONSE; import static com.hazelcast.spi.impl.SpiDataSerializerHook.NORMAL_RESPONSE; import static com.hazelcast.spi.impl.operationservice.impl.responses.NormalResponse.OFFSET_BACKUP_ACKS; import static com.hazelcast.spi.impl.operationservice.impl.responses.Response.OFFSET_CALL_ID; import static com.hazelcast.spi.impl.operationservice.impl.responses.Response.OFFSET_TYPE_ID; /** * Responsible for handling responses for invocations. Based on the content of the response packet, it will lookup the * Invocation from the InvocationRegistry and notify the Invocation. */ public final class InboundResponseHandler implements PacketHandler, MetricsProvider { private final ILogger logger; private final InternalSerializationService serializationService; private final InvocationRegistry invocationRegistry; private final NodeEngineImpl nodeEngine; @Probe(name = "responses[normal]", level = MANDATORY) private final SwCounter responsesNormal = newSwCounter(); @Probe(name = "responses[timeout]", level = MANDATORY) private final SwCounter responsesTimeout = newSwCounter(); @Probe(name = "responses[backup]", level = MANDATORY) private final MwCounter responsesBackup = newMwCounter(); @Probe(name = "responses[error]", level = MANDATORY) private final SwCounter responsesError = newSwCounter(); @Probe(name = "responses[missing]", level = MANDATORY) private final MwCounter responsesMissing = newMwCounter(); private final boolean useBigEndian; InboundResponseHandler(ILogger logger, InternalSerializationService serializationService, InvocationRegistry invocationRegistry, NodeEngineImpl nodeEngine) { this.logger = logger; this.useBigEndian = serializationService.getByteOrder() == ByteOrder.BIG_ENDIAN; this.serializationService = serializationService; this.invocationRegistry = invocationRegistry; this.nodeEngine = nodeEngine; } @Override public void provideMetrics(MetricsRegistry registry) { registry.scanAndRegister(this, "operation.invocations"); } @Override public void handle(Packet packet) throws Exception { byte[] bytes = packet.toByteArray(); int typeId = Bits.readInt(bytes, OFFSET_TYPE_ID, useBigEndian); long callId = Bits.readLong(bytes, OFFSET_CALL_ID, useBigEndian); Address sender = packet.getConn().getEndPoint(); try { switch (typeId) { case NORMAL_RESPONSE: byte backupAcks = bytes[OFFSET_BACKUP_ACKS]; notifyNormalResponse(callId, packet, backupAcks, sender); break; case BACKUP_ACK_RESPONSE: notifyBackupComplete(callId); break; case CALL_TIMEOUT_RESPONSE: notifyCallTimeout(callId, sender); break; case ERROR_RESPONSE: ErrorResponse errorResponse = serializationService.toObject(packet); notifyErrorResponse(callId, errorResponse.getCause(), sender); break; default: logger.severe("Unrecognized type: " + typeId); } } catch (Throwable e) { logger.severe("While processing response...", e); } } public void notifyBackupComplete(long callId) { responsesBackup.inc(); try { Invocation invocation = invocationRegistry.get(callId); // It can happen that a backup response is send without the Invocation being available anymore. // This is because the InvocationRegistry will automatically release invocations where the backup is // taking too much time. if (invocation == null) { if (logger.isFinestEnabled()) { logger.finest("No Invocation found for backup response with callId " + callId); } return; } invocation.notifyBackupComplete(); } catch (Exception e) { ReplicaErrorLogger.log(e, logger); } } void notifyErrorResponse(long callId, Object cause, Address sender) { responsesError.inc(); Invocation invocation = invocationRegistry.get(callId); if (invocation == null) { responsesMissing.inc(); if (nodeEngine.isRunning()) { logger.warning("No Invocation found for error response with callId: " + callId + " sent from " + sender); } return; } invocation.notifyError(cause); } void notifyNormalResponse(long callId, Object value, int backupCount, Address sender) { responsesNormal.inc(); Invocation invocation = invocationRegistry.get(callId); if (invocation == null) { responsesMissing.inc(); if (nodeEngine.isRunning()) { logger.warning("No Invocation found for normal response with callId " + callId + " sent from " + sender); } return; } invocation.notifyNormalResponse(value, backupCount); } void notifyCallTimeout(long callId, Address sender) { responsesTimeout.inc(); Invocation invocation = invocationRegistry.get(callId); if (invocation == null) { responsesMissing.inc(); if (nodeEngine.isRunning()) { logger.warning("No Invocation found for call timeout response with callId" + callId + " sent from " + sender); } return; } invocation.notifyCallTimeout(); } }