/**
* Copyright 2016 LinkedIn Corp. 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.
*/
package com.github.ambry.protocol;
import com.github.ambry.clustermap.ClusterMap;
import com.github.ambry.clustermap.PartitionId;
import com.github.ambry.commons.ServerErrorCode;
import com.github.ambry.store.FindToken;
import com.github.ambry.store.FindTokenFactory;
import com.github.ambry.store.MessageInfo;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.List;
/**
* Contains the token, messageInfoList, remoteReplicaLag for a local partition. This is used
* by replica metadata response to specify information for a partition
*/
public class ReplicaMetadataResponseInfo {
private final FindToken token;
private final MessageInfoListSerde messageInfoListSerDe;
private final int messageInfoListSize;
private final long remoteReplicaLagInBytes;
private final PartitionId partitionId;
private final ServerErrorCode errorCode;
private static final int Error_Size_InBytes = 2;
private static final int Remote_Replica_Lag_Size_In_Bytes = 8;
private ReplicaMetadataResponseInfo(PartitionId partitionId, FindToken findToken, List<MessageInfo> messageInfoList,
long remoteReplicaLagInBytes, short replicaMetadataResponseVersion) {
if (partitionId == null || findToken == null || messageInfoList == null) {
throw new IllegalArgumentException(
"Invalid partition or token or message info list for ReplicaMetadataResponseInfo");
}
this.partitionId = partitionId;
this.remoteReplicaLagInBytes = remoteReplicaLagInBytes;
messageInfoListSerDe =
new MessageInfoListSerde(messageInfoList, getMessageInfoListVersion(replicaMetadataResponseVersion));
messageInfoListSize = messageInfoListSerDe.getMessageInfoListSize();
this.token = findToken;
this.errorCode = ServerErrorCode.No_Error;
}
public ReplicaMetadataResponseInfo(PartitionId partitionId, ServerErrorCode errorCode) {
if (partitionId == null) {
throw new IllegalArgumentException("Invalid partition for ReplicaMetadataResponseInfo");
}
this.partitionId = partitionId;
this.errorCode = errorCode;
this.token = null;
this.messageInfoListSerDe = null;
this.messageInfoListSize = 0;
this.remoteReplicaLagInBytes = 0;
}
public ReplicaMetadataResponseInfo(PartitionId partitionId, FindToken findToken, List<MessageInfo> messageInfoList,
long remoteReplicaLagInBytes) {
this(partitionId, findToken, messageInfoList, remoteReplicaLagInBytes, ReplicaMetadataResponse.getCurrentVersion());
}
public PartitionId getPartitionId() {
return partitionId;
}
public List<MessageInfo> getMessageInfoList() {
return messageInfoListSerDe.getMessageInfoList();
}
public FindToken getFindToken() {
return token;
}
public long getRemoteReplicaLagInBytes() {
return remoteReplicaLagInBytes;
}
public ServerErrorCode getError() {
return errorCode;
}
public static ReplicaMetadataResponseInfo readFrom(DataInputStream stream, FindTokenFactory factory,
ClusterMap clusterMap, short replicaMetadataResponseVersion) throws IOException {
PartitionId partitionId = clusterMap.getPartitionIdFromStream(stream);
ServerErrorCode error = ServerErrorCode.values()[stream.readShort()];
if (error != ServerErrorCode.No_Error) {
return new ReplicaMetadataResponseInfo(partitionId, error);
} else {
FindToken token = factory.getFindToken(stream);
List<MessageInfo> messageInfoList = MessageInfoListSerde.deserializeMessageInfoList(stream, clusterMap,
getMessageInfoListVersion(replicaMetadataResponseVersion));
long remoteReplicaLag = stream.readLong();
return new ReplicaMetadataResponseInfo(partitionId, token, messageInfoList, remoteReplicaLag,
replicaMetadataResponseVersion);
}
}
public void writeTo(ByteBuffer buffer) {
buffer.put(partitionId.getBytes());
buffer.putShort((short) errorCode.ordinal());
if (errorCode == ServerErrorCode.No_Error) {
buffer.put(token.toBytes());
messageInfoListSerDe.serializeMessageInfoList(buffer);
buffer.putLong(remoteReplicaLagInBytes);
}
}
public long sizeInBytes() {
return (token == null ? 0 : (token.toBytes().length + Remote_Replica_Lag_Size_In_Bytes + messageInfoListSize))
+ +partitionId.getBytes().length + Error_Size_InBytes;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(partitionId);
sb.append(" ServerErrorCode=").append(errorCode);
if (errorCode == ServerErrorCode.No_Error) {
sb.append(" Token=").append(token);
sb.append(" MessageInfoList=").append(messageInfoListSerDe.getMessageInfoList());
sb.append(" RemoteReplicaLagInBytes=").append(remoteReplicaLagInBytes);
}
return sb.toString();
}
/**
* Return the MessageInfoList version to use for the given {@link ReplicaMetadataResponse} version
* @param replicaMetadataResponseVersion the {@link ReplicaMetadataResponse} version
* @return the MessageInfoList version to use for the given {@link ReplicaMetadataResponse} version
*/
private static short getMessageInfoListVersion(short replicaMetadataResponseVersion) {
switch (replicaMetadataResponseVersion) {
case ReplicaMetadataResponse.Replica_Metadata_Response_Version_V1:
return MessageInfoListSerde.MessageInfoListVersion_V1;
case ReplicaMetadataResponse.Replica_Metadata_Response_Version_V2:
return MessageInfoListSerde.MessageInfoListVersion_V2;
default:
throw new IllegalArgumentException(
"Unknown ReplicaMetadataResponse version encountered: " + replicaMetadataResponseVersion);
}
}
}