/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.apache.hadoop.hdfs.protocol.datatransfer; import static org.apache.hadoop.hdfs.protocolPB.PBHelper.vintPrefixed; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import com.google.common.collect.Lists; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.hdfs.HdfsConfiguration; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_OOB_TIMEOUT_KEY; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_DATANODE_OOB_TIMEOUT_DEFAULT; import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.PipelineAckProto; import org.apache.hadoop.hdfs.protocol.proto.DataTransferProtos.Status; import com.google.protobuf.TextFormat; import org.apache.hadoop.hdfs.util.LongBitFormat; /** Pipeline Acknowledgment **/ @InterfaceAudience.Private @InterfaceStability.Evolving public class PipelineAck { PipelineAckProto proto; public final static long UNKOWN_SEQNO = -2; final static int OOB_START = Status.OOB_RESTART_VALUE; // the first OOB type final static int OOB_END = Status.OOB_RESERVED3_VALUE; // the last OOB type final static int NUM_OOB_TYPES = OOB_END - OOB_START + 1; // place holder for timeout value of each OOB type final static long[] OOB_TIMEOUT; public enum ECN { DISABLED(0), SUPPORTED(1), SUPPORTED2(2), CONGESTED(3); private final int value; private static final ECN[] VALUES = values(); static ECN valueOf(int value) { return VALUES[value]; } ECN(int value) { this.value = value; } public int getValue() { return value; } } private enum StatusFormat { STATUS(null, 4), RESERVED(STATUS.BITS, 1), ECN_BITS(RESERVED.BITS, 2); private final LongBitFormat BITS; StatusFormat(LongBitFormat prev, int bits) { BITS = new LongBitFormat(name(), prev, bits, 0); } static Status getStatus(int header) { return Status.valueOf((int) STATUS.BITS.retrieve(header)); } static ECN getECN(int header) { return ECN.valueOf((int) ECN_BITS.BITS.retrieve(header)); } public static int setStatus(int old, Status status) { return (int) STATUS.BITS.combine(status.getNumber(), old); } public static int setECN(int old, ECN ecn) { return (int) ECN_BITS.BITS.combine(ecn.getValue(), old); } } static { OOB_TIMEOUT = new long[NUM_OOB_TYPES]; HdfsConfiguration conf = new HdfsConfiguration(); String[] ele = conf.get(DFS_DATANODE_OOB_TIMEOUT_KEY, DFS_DATANODE_OOB_TIMEOUT_DEFAULT).split(","); for (int i = 0; i < NUM_OOB_TYPES; i++) { OOB_TIMEOUT[i] = (i < ele.length) ? Long.parseLong(ele[i]) : 0; } } /** default constructor **/ public PipelineAck() { } /** * Constructor assuming no next DN in pipeline * @param seqno sequence number * @param replies an array of replies */ public PipelineAck(long seqno, int[] replies) { this(seqno, replies, 0L); } /** * Constructor * @param seqno sequence number * @param replies an array of replies * @param downstreamAckTimeNanos ack RTT in nanoseconds, 0 if no next DN in pipeline */ public PipelineAck(long seqno, int[] replies, long downstreamAckTimeNanos) { ArrayList<Status> statusList = Lists.newArrayList(); ArrayList<Integer> flagList = Lists.newArrayList(); for (int r : replies) { statusList.add(StatusFormat.getStatus(r)); flagList.add(r); } proto = PipelineAckProto.newBuilder() .setSeqno(seqno) .addAllReply(statusList) .addAllFlag(flagList) .setDownstreamAckTimeNanos(downstreamAckTimeNanos) .build(); } /** * Get the sequence number * @return the sequence number */ public long getSeqno() { return proto.getSeqno(); } /** * Get the number of replies * @return the number of replies */ public short getNumOfReplies() { return (short)proto.getReplyCount(); } /** * get the header flag of ith reply */ public int getHeaderFlag(int i) { if (proto.getFlagCount() > 0) { return proto.getFlag(i); } else { return combineHeader(ECN.DISABLED, proto.getReply(i)); } } public int getFlag(int i) { return proto.getFlag(i); } /** * Get the time elapsed for downstream ack RTT in nanoseconds * @return time elapsed for downstream ack in nanoseconds, 0 if no next DN in pipeline */ public long getDownstreamAckTimeNanos() { return proto.getDownstreamAckTimeNanos(); } /** * Check if this ack contains error status * @return true if all statuses are SUCCESS */ public boolean isSuccess() { for (Status s : proto.getReplyList()) { if (s != Status.SUCCESS) { return false; } } return true; } /** * Returns the OOB status if this ack contains one. * @return null if it is not an OOB ack. */ public Status getOOBStatus() { // Normal data transfer acks will have a valid sequence number, so // this will return right away in most cases. if (getSeqno() != UNKOWN_SEQNO) { return null; } for (Status s : proto.getReplyList()) { // The following check is valid because protobuf guarantees to // preserve the ordering of enum elements. if (s.getNumber() >= OOB_START && s.getNumber() <= OOB_END) { return s; } } return null; } /** * Get the timeout to be used for transmitting the OOB type * @return the timeout in milliseconds */ public static long getOOBTimeout(Status status) throws IOException { int index = status.getNumber() - OOB_START; if (index >= 0 && index < NUM_OOB_TYPES) { return OOB_TIMEOUT[index]; } // Not an OOB. throw new IOException("Not an OOB status: " + status); } /** Get the Restart OOB ack status */ public static Status getRestartOOBStatus() { return Status.OOB_RESTART; } /** return true if it is the restart OOB status code */ public static boolean isRestartOOBStatus(Status st) { return st.equals(Status.OOB_RESTART); } /**** Writable interface ****/ public void readFields(InputStream in) throws IOException { proto = PipelineAckProto.parseFrom(vintPrefixed(in)); } public void write(OutputStream out) throws IOException { proto.writeDelimitedTo(out); } @Override //Object public String toString() { return TextFormat.shortDebugString(proto); } public static Status getStatusFromHeader(int header) { return StatusFormat.getStatus(header); } public static ECN getECNFromHeader(int header) { return StatusFormat.getECN(header); } public static int setStatusForHeader(int old, Status status) { return StatusFormat.setStatus(old, status); } public static int combineHeader(ECN ecn, Status status) { int header = 0; header = StatusFormat.setStatus(header, status); header = StatusFormat.setECN(header, ecn); return header; } }