alluxio.worker.netty.DataServerShortCircuitReadHandler.java Source code

Java tutorial

Introduction

Here is the source code for alluxio.worker.netty.DataServerShortCircuitReadHandler.java

Source

/*
 * 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.BlockDoesNotExistException;
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.BlockLockManager;
import alluxio.worker.block.BlockWorker;

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ExecutorService;

import javax.annotation.concurrent.NotThreadSafe;

/**
 * Netty handler that handles short circuit read requests.
 */
@NotThreadSafe
class DataServerShortCircuitReadHandler extends ChannelInboundHandlerAdapter {
    private static final Logger LOG = LoggerFactory.getLogger(DataServerShortCircuitReadHandler.class);

    /** Executor service for block opens. */
    private final ExecutorService mBlockOpenExecutor;
    private final StorageTierAssoc mStorageTierAssoc = new WorkerStorageTierAssoc();
    /** The block worker. */
    private final BlockWorker mWorker;
    /** The lock Id of the block being read. */
    private long mLockId;
    private long mSessionId;

    /**
     * Creates an instance of {@link DataServerShortCircuitReadHandler}.
     *
     * @param blockWorker the block worker
     */
    DataServerShortCircuitReadHandler(ExecutorService service, BlockWorker blockWorker) {
        mBlockOpenExecutor = service;
        mWorker = blockWorker;
        mLockId = BlockLockManager.INVALID_LOCK_ID;
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) {
        if (!(msg instanceof RPCProtoMessage)) {
            ctx.fireChannelRead(msg);
            return;
        }

        ProtoMessage message = ((RPCProtoMessage) msg).getMessage();
        if (message.isLocalBlockOpenRequest()) {
            handleBlockOpenRequest(ctx, message.asLocalBlockOpenRequest());
        } else if (message.isLocalBlockCloseRequest()) {
            handleBlockCloseRequest(ctx, message.asLocalBlockCloseRequest());
        } 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 (mLockId != BlockLockManager.INVALID_LOCK_ID) {
            try {
                mWorker.unlockBlock(mLockId);
            } catch (BlockDoesNotExistException e) {
                LOG.warn("Failed to unlock lock {} with error {}.", mLockId, e.getMessage());
            }
            mWorker.cleanupSession(mSessionId);
        }
        ctx.fireChannelUnregistered();
    }

    /**
     * Runnable for handling the expensive open block call logic.
     */
    final class BlockOpenRequestHandler implements Runnable {
        Protocol.LocalBlockOpenRequest mRequest;
        ChannelHandlerContext mContext;

        private BlockOpenRequestHandler(ChannelHandlerContext ctx, Protocol.LocalBlockOpenRequest req) {
            mContext = ctx;
            mRequest = req;
        }

        @Override
        public void run() {
            RpcUtils.nettyRPCAndLog(LOG, new RpcUtils.NettyRPCCallable<Void>() {
                @Override
                public Void call() throws Exception {
                    if (mLockId == BlockLockManager.INVALID_LOCK_ID) {
                        mSessionId = IdUtils.createSessionId();
                        // TODO(calvin): Update the locking logic so this can be done better
                        if (mRequest.getPromote()) {
                            try {
                                mWorker.moveBlock(mSessionId, mRequest.getBlockId(), mStorageTierAssoc.getAlias(0));
                            } catch (Exception e) {
                                LOG.warn("Failed to promote block {}: {}", mRequest.getBlockId(), e.getMessage());
                            }
                        }
                        mLockId = mWorker.lockBlock(mSessionId, mRequest.getBlockId());
                        mWorker.accessBlock(mSessionId, mRequest.getBlockId());
                    } else {
                        LOG.warn("Lock block {} without releasing previous block lock {}.", mRequest.getBlockId(),
                                mLockId);
                        throw new InvalidWorkerStateException(
                                ExceptionMessage.LOCK_NOT_RELEASED.getMessage(mLockId));
                    }
                    Protocol.LocalBlockOpenResponse response = Protocol.LocalBlockOpenResponse.newBuilder()
                            .setPath(mWorker.readBlock(mSessionId, mRequest.getBlockId(), mLockId)).build();
                    mContext.writeAndFlush(new RPCProtoMessage(new ProtoMessage(response)));

                    return null;
                }

                @Override
                public void exceptionCaught(Throwable e) {
                    if (mLockId != BlockLockManager.INVALID_LOCK_ID) {
                        try {
                            mWorker.unlockBlock(mLockId);
                        } catch (BlockDoesNotExistException ee) {
                            LOG.error("Failed to unlock block {}.", mRequest.getBlockId(), e);
                        }
                        mLockId = BlockLockManager.INVALID_LOCK_ID;
                    }
                    mContext.writeAndFlush(RPCProtoMessage.createResponse(AlluxioStatusException.fromThrowable(e)));
                }

                @Override
                public String toString() {
                    return String.format("Open block: %s", mRequest.toString());
                }
            });
        }
    }

    /**
     * Handles {@link Protocol.LocalBlockOpenRequest}. Since the open can be expensive, the work is
     * delegated to a threadpool. No exceptions should be thrown.
     *
     * @param ctx the channel handler context
     * @param request the local block open request
     */
    private void handleBlockOpenRequest(final ChannelHandlerContext ctx,
            final Protocol.LocalBlockOpenRequest request) {
        BlockOpenRequestHandler handler = new BlockOpenRequestHandler(ctx, request);
        mBlockOpenExecutor.submit(handler);
    }

    /**
     * Handles {@link Protocol.LocalBlockCloseRequest}. No exceptions should be thrown.
     *
     * @param ctx the channel handler context
     * @param request the local block close request
     */
    private void handleBlockCloseRequest(final ChannelHandlerContext ctx,
            final Protocol.LocalBlockCloseRequest request) {
        RpcUtils.nettyRPCAndLog(LOG, new RpcUtils.NettyRPCCallable<Void>() {

            @Override
            public Void call() throws Exception {
                if (mLockId != BlockLockManager.INVALID_LOCK_ID) {
                    mWorker.unlockBlock(mLockId);
                    mLockId = BlockLockManager.INVALID_LOCK_ID;
                } else {
                    LOG.warn("Close a closed block {}.", request.getBlockId());
                }
                ctx.writeAndFlush(RPCProtoMessage.createOkResponse(null));
                return null;
            }

            @Override
            public void exceptionCaught(Throwable e) {
                ctx.writeAndFlush(RPCProtoMessage.createResponse(AlluxioStatusException.fromThrowable(e)));
                mLockId = BlockLockManager.INVALID_LOCK_ID;
            }

            @Override
            public String toString() {
                return String.format("Close block: %s", request.toString());
            }
        });
    }
}