/**
* 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.qjournal.server;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URL;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hdfs.qjournal.protocol.JournalConfigKeys;
import org.apache.hadoop.hdfs.qjournal.protocol.JournalRequestInfo;
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocol;
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.GetEditLogManifestResponseProto;
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.GetImageManifestResponseProto;
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.GetJournalStateResponseProto;
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.GetStorageStateProto;
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.NewEpochResponseProto;
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.PrepareRecoveryResponseProto;
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.SegmentStateProto;
import org.apache.hadoop.hdfs.qjournal.protocol.RequestInfo;
import org.apache.hadoop.hdfs.server.common.HdfsConstants.StartupOption;
import org.apache.hadoop.hdfs.server.common.HdfsConstants.Transition;
import org.apache.hadoop.hdfs.server.protocol.NamespaceInfo;
import org.apache.hadoop.hdfs.server.protocol.RemoteEditLogManifest;
import org.apache.hadoop.io.MD5Hash;
import org.apache.hadoop.io.ShortVoid;
import org.apache.hadoop.ipc.FastProtocolRegister;
import org.apache.hadoop.ipc.FastProtocolRegister.FastProtocolId;
import org.apache.hadoop.ipc.ProtocolSignature;
import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.ipc.RPC.Server;
import org.apache.hadoop.ipc.RPC.VersionIncompatible;
import org.apache.hadoop.net.NetUtils;
import org.apache.hadoop.util.InjectionHandler;
import org.apache.hadoop.hdfs.util.InjectionEvent;
public class JournalNodeRpcServer implements QJournalProtocol {
public static final Log LOG = LogFactory.getLog(Journal.class);
private static final int HANDLER_COUNT = 5;
private final JournalNode jn;
private final Server server;
private final Configuration conf;
JournalNodeRpcServer(Configuration conf, JournalNode jn) throws IOException {
this.jn = jn;
Configuration confCopy = new Configuration(conf);
// Ensure that nagling doesn't kick in, which could cause latency issues.
confCopy.setBoolean("ipc.server.tcpnodelay", true);
// reader threads will handle the RPC calls
confCopy.setBoolean("ipc.direct.handling", true);
// set the number of reader threads, should be at least the number of
// served namenodes
confCopy.setInt(Server.IPC_SERVER_RPC_READ_THREADS_KEY, confCopy.getInt(
JournalConfigKeys.DFS_QJOURNAL_IPC_READER_KEY,
JournalConfigKeys.DFS_QJOURNAL_IPC_READER_DEFAULT));
InetSocketAddress addr = getAddress(confCopy);
this.server = RPC.getServer(this, addr.getAddress().getHostAddress(), addr.getPort(),
HANDLER_COUNT, false, confCopy, false);
this.conf = confCopy;
}
void start() throws IOException {
this.server.start();
}
public InetSocketAddress getAddress() {
return server.getListenerAddress();
}
void join() throws InterruptedException {
this.server.join();
}
void stop() {
this.server.stop();
}
static InetSocketAddress getAddress(Configuration conf) {
String addr = conf.get(
JournalConfigKeys.DFS_JOURNALNODE_RPC_ADDRESS_KEY,
JournalConfigKeys.DFS_JOURNALNODE_RPC_ADDRESS_DEFAULT);
return NetUtils.createSocketAddr(addr, 0);
}
@Override
public boolean isJournalFormatted(byte[] journalId) throws IOException {
return jn.getOrCreateJournal(journalId).isJournalFormatted();
}
@Override
public boolean isImageFormatted(byte[] journalId) throws IOException {
return jn.getOrCreateJournal(journalId).isImageFormatted();
}
@Override
public GetJournalStateResponseProto getJournalState(byte[] journalId)
throws IOException {
long epoch = jn.getOrCreateJournal(journalId).getLastPromisedEpoch();
GetJournalStateResponseProto ret = new GetJournalStateResponseProto();
ret.setLastPromisedEpoch(epoch);
ret.setHttpPort(jn.getBoundHttpAddress().getPort());
return ret;
}
@Override
public NewEpochResponseProto newEpoch(byte[] journalId,
NamespaceInfo nsInfo,
long epoch) throws IOException {
NewEpochResponseProto p = jn.getOrCreateJournal(journalId).newEpoch(nsInfo, epoch);
return p;
}
@Override
public void transitionJournal(byte[] journalId, NamespaceInfo nsInfo,
Transition transition, StartupOption startOpt) throws IOException {
jn.getOrCreateJournal(journalId).transitionJournal(nsInfo, transition,
startOpt);
}
@Override
public void transitionImage(byte[] journalId, NamespaceInfo nsInfo,
Transition transition, StartupOption startOpt) throws IOException {
jn.getOrCreateJournal(journalId).transitionImage(nsInfo, transition,
startOpt);
}
@Override
public ShortVoid journal(JournalRequestInfo reqInfo) throws IOException {
InjectionHandler.processEventIO(InjectionEvent.QJM_JOURNALNODE_JOURNAL,
conf, this.server.getListenerAddress());
long startTime = System.currentTimeMillis();
ShortVoid ret = jn.getOrCreateJournal(reqInfo.getJournalId()).journal(reqInfo,
reqInfo.getSegmentTxId(), reqInfo.getFirstTxId(), reqInfo.getNumTxns(),
reqInfo.getRecords());
if (LOG.isDebugEnabled()) {
LOG.debug("Time spent in journal: " + (System.currentTimeMillis() - startTime) +
". RequestInfo: " + reqInfo.toString());
}
return ret;
}
@Override
public void heartbeat(RequestInfo reqInfo) throws IOException {
jn.getOrCreateJournal(reqInfo.getJournalId())
.heartbeat(reqInfo);
}
@Override
public void startLogSegment(RequestInfo reqInfo, long txid)
throws IOException {
InjectionHandler.processEventIO(
InjectionEvent.QJM_JOURNALNODE_STARTSEGMENT, conf);
jn.getOrCreateJournal(reqInfo.getJournalId())
.startLogSegment(reqInfo, txid);
}
@Override
public void finalizeLogSegment(RequestInfo reqInfo, long startTxId,
long endTxId) throws IOException {
jn.getOrCreateJournal(reqInfo.getJournalId())
.finalizeLogSegment(reqInfo, startTxId, endTxId);
}
@Override
public void purgeLogsOlderThan(RequestInfo reqInfo, long minTxIdToKeep)
throws IOException {
jn.getOrCreateJournal(reqInfo.getJournalId())
.purgeLogsOlderThan(reqInfo, minTxIdToKeep);
}
@Override
public GetEditLogManifestResponseProto getEditLogManifest(byte[] jid,
long sinceTxId) throws IOException {
RemoteEditLogManifest manifest = jn.getOrCreateJournal(jid)
.getEditLogManifest(sinceTxId);
GetEditLogManifestResponseProto ret = new GetEditLogManifestResponseProto();
ret.setLogs(manifest.getLogs());
ret.setHttpPort(jn.getBoundHttpAddress().getPort());
if (JournalNode.LOG.isDebugEnabled()) {
JournalNode.LOG.info("Returning manifest " + manifest.toString());
}
return ret;
}
@Override
public PrepareRecoveryResponseProto prepareRecovery(RequestInfo reqInfo,
long segmentTxId) throws IOException {
return jn.getOrCreateJournal(reqInfo.getJournalId())
.prepareRecovery(reqInfo, segmentTxId);
}
@Override
public void acceptRecovery(RequestInfo reqInfo, SegmentStateProto log,
String fromUrl) throws IOException {
jn.getOrCreateJournal(reqInfo.getJournalId())
.acceptRecovery(reqInfo, log, new URL(fromUrl));
}
@Override
public ProtocolSignature getProtocolSignature(String protocol,
long clientVersion, int clientMethodsHash) throws IOException {
return ProtocolSignature.getProtocolSignature(
this, protocol, clientVersion, clientMethodsHash);
}
@Override
public long getProtocolVersion(String protocol, long clientVersion)
throws VersionIncompatible, IOException {
if (protocol.equals(QJournalProtocol.class.getName())){
return QJournalProtocol.versionID;
} else {
throw new IOException("Unknown protocol: " + protocol);
}
}
@Override
public GetStorageStateProto analyzeJournalStorage(byte[] jid)
throws IOException {
return jn.getOrCreateJournal(jid).analyzeJournalStorage();
}
@Override
public void saveDigestAndRenameCheckpointImage(RequestInfo reqInfo,
long txid, MD5Hash digest) throws IOException {
jn.getOrCreateJournal(reqInfo.getJournalId())
.saveDigestAndRenameCheckpointImage(txid, digest);
}
@Override
public GetImageManifestResponseProto getImageManifest(byte[] jid,
long sinceTxId) throws IOException {
GetImageManifestResponseProto ret = new GetImageManifestResponseProto();
ret.setImages(jn.getOrCreateJournal(jid).getImageManifest(sinceTxId)
.getImages());
return ret;
}
@Override
public GetStorageStateProto analyzeImageStorage(byte[] jid)
throws IOException {
return jn.getOrCreateJournal(jid).analyzeImageStorage();
}
// register fast protocol
public static void init() {
try {
FastProtocolRegister.register(FastProtocolId.SERIAL_VERSION_ID_1,
QJournalProtocol.class.getMethod("journal",
JournalRequestInfo.class));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}