/* * The Alluxio Open Foundation licenses this work under the Apache License, version 2.0 * (the "License"). You may not use this work except in compliance with the License, which is * available at www.apache.org/licenses/LICENSE-2.0 * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, * either express or implied, as more fully set forth in the License. * * See the NOTICE file distributed with this work for information regarding copyright ownership. */ package alluxio.worker.netty; import alluxio.RpcUtils; import alluxio.StorageTierAssoc; import alluxio.WorkerStorageTierAssoc; import alluxio.exception.ExceptionMessage; import alluxio.exception.InvalidWorkerStateException; import alluxio.exception.status.AlluxioStatusException; import alluxio.network.protocol.RPCProtoMessage; import alluxio.proto.dataserver.Protocol; import alluxio.util.IdUtils; import alluxio.util.proto.ProtoMessage; import alluxio.worker.block.BlockWorker; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.concurrent.NotThreadSafe; /** * Netty handler that handles short circuit read requests. */ @NotThreadSafe class DataServerShortCircuitWriteHandler extends ChannelInboundHandlerAdapter { private static final Logger LOG = LoggerFactory.getLogger(DataServerShortCircuitWriteHandler.class); private static final long INVALID_SESSION_ID = -1; /** The block worker. */ private final BlockWorker mBlockWorker; /** An object storing the mapping of tier aliases to ordinals. */ private final StorageTierAssoc mStorageTierAssoc = new WorkerStorageTierAssoc(); private long mSessionId = INVALID_SESSION_ID; /** * Creates an instance of {@link DataServerShortCircuitWriteHandler}. * * @param blockWorker the block worker */ DataServerShortCircuitWriteHandler(BlockWorker blockWorker) { mBlockWorker = blockWorker; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) { if (!(msg instanceof RPCProtoMessage)) { ctx.fireChannelRead(msg); return; } ProtoMessage message = ((RPCProtoMessage) msg).getMessage(); if (message.isLocalBlockCreateRequest()) { handleBlockCreateRequest(ctx, message.asLocalBlockCreateRequest()); } else if (message.isLocalBlockCompleteRequest()) { handleBlockCompleteRequest(ctx, message.asLocalBlockCompleteRequest()); } else { ctx.fireChannelRead(msg); } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) { // The RPC handlers do not throw exceptions. All the exception seen here is either // network exception or some runtime exception (e.g. NullPointerException). LOG.error("Failed to handle RPCs.", throwable); ctx.close(); } @Override public void channelUnregistered(ChannelHandlerContext ctx) { if (mSessionId != INVALID_SESSION_ID) { mBlockWorker.cleanupSession(mSessionId); } ctx.fireChannelUnregistered(); } /** * Handles {@link Protocol.LocalBlockCreateRequest} to create block. No exceptions should be * thrown. * * @param ctx the channel handler context * @param request the local block create request */ private void handleBlockCreateRequest(final ChannelHandlerContext ctx, final Protocol.LocalBlockCreateRequest request) { RpcUtils.nettyRPCAndLog(LOG, new RpcUtils.NettyRPCCallable<Void>() { @Override public Void call() throws Exception { if (request.getOnlyReserveSpace()) { mBlockWorker.requestSpace(mSessionId, request.getBlockId(), request.getSpaceToReserve()); ctx.writeAndFlush(RPCProtoMessage.createOkResponse(null)); } else { if (mSessionId == INVALID_SESSION_ID) { mSessionId = IdUtils.createSessionId(); String path = mBlockWorker.createBlock(mSessionId, request.getBlockId(), mStorageTierAssoc.getAlias(request.getTier()), request.getSpaceToReserve()); Protocol.LocalBlockCreateResponse response = Protocol.LocalBlockCreateResponse.newBuilder().setPath(path).build(); ctx.writeAndFlush(new RPCProtoMessage(new ProtoMessage(response))); } else { LOG.warn("Create block {} without closing the previous session {}.", request.getBlockId(), mSessionId); throw new InvalidWorkerStateException( ExceptionMessage.SESSION_NOT_CLOSED.getMessage(mSessionId)); } } return null; } @Override public void exceptionCaught(Throwable throwable) { ctx.writeAndFlush( RPCProtoMessage.createResponse(AlluxioStatusException.fromThrowable(throwable))); } @Override public String toString() { if (request.getOnlyReserveSpace()) { return String.format("Reserve space: %s", request.toString()); } else { return String.format("Create block: %s", request.toString()); } } }); } /** * Handles {@link Protocol.LocalBlockCompleteRequest}. No exceptions should be thrown. * * @param ctx the channel handler context * @param request the local block close request */ private void handleBlockCompleteRequest(final ChannelHandlerContext ctx, final Protocol.LocalBlockCompleteRequest request) { RpcUtils.nettyRPCAndLog(LOG, new RpcUtils.NettyRPCCallable<Void>() { @Override public Void call() throws Exception { if (request.getCancel()) { mBlockWorker.abortBlock(mSessionId, request.getBlockId()); } else { mBlockWorker.commitBlock(mSessionId, request.getBlockId()); } mSessionId = INVALID_SESSION_ID; ctx.writeAndFlush(RPCProtoMessage.createOkResponse(null)); return null; } @Override public void exceptionCaught(Throwable throwable) { ctx.writeAndFlush( RPCProtoMessage.createResponse(AlluxioStatusException.fromThrowable(throwable))); mSessionId = INVALID_SESSION_ID; } @Override public String toString() { if (request.getCancel()) { return String.format("Abort block: %s", request.toString()); } else { return String.format("Commit block: %s", request.toString()); } } }); } }