Skip to content

Commit e2a0dca

Browse files
authored
HDFS-16690. Automatically format unformatted JNs with JournalNodeSyncer (#6925). Contributed by Aswin M Prabhu.
Signed-off-by: He Xiaoqiao <hexiaoqiao@apache.org>
1 parent e000cbf commit e2a0dca

File tree

9 files changed

+202
-11
lines changed

9 files changed

+202
-11
lines changed

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSConfigKeys.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1471,6 +1471,9 @@ public class DFSConfigKeys extends CommonConfigurationKeys {
14711471
public static final String DFS_JOURNALNODE_SYNC_INTERVAL_KEY =
14721472
"dfs.journalnode.sync.interval";
14731473
public static final long DFS_JOURNALNODE_SYNC_INTERVAL_DEFAULT = 2*60*1000L;
1474+
public static final String DFS_JOURNALNODE_ENABLE_SYNC_FORMAT_KEY =
1475+
"dfs.journalnode.enable.sync.format";
1476+
public static final boolean DFS_JOURNALNODE_ENABLE_SYNC_FORMAT_DEFAULT = false;
14741477
public static final String DFS_JOURNALNODE_EDIT_CACHE_SIZE_KEY =
14751478
"dfs.journalnode.edit-cache-size.bytes";
14761479

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/protocol/InterQJournalProtocol.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
import org.apache.hadoop.classification.InterfaceAudience;
2222
import org.apache.hadoop.hdfs.DFSConfigKeys;
23+
import org.apache.hadoop.hdfs.protocol.proto.HdfsServerProtos.StorageInfoProto;
2324
import org.apache.hadoop.hdfs.qjournal.server.JournalNode;
2425
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.GetEditLogManifestResponseProto;
2526
import org.apache.hadoop.security.KerberosInfo;
@@ -51,4 +52,13 @@ GetEditLogManifestResponseProto getEditLogManifestFromJournal(
5152
String jid, String nameServiceId, long sinceTxId, boolean inProgressOk)
5253
throws IOException;
5354

55+
/**
56+
* Get the storage info for the specified journal.
57+
* @param jid the journal identifier
58+
* @param nameServiceId the name service id
59+
* @return the storage info object
60+
*/
61+
StorageInfoProto getStorageInfo(String jid, String nameServiceId)
62+
throws IOException;
63+
5464
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/protocolPB/InterQJournalProtocolServerSideTranslatorPB.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424

2525
import org.apache.hadoop.classification.InterfaceAudience;
2626
import org.apache.hadoop.hdfs.qjournal.protocol.InterQJournalProtocol;
27+
import org.apache.hadoop.hdfs.protocol.proto.HdfsServerProtos.StorageInfoProto;
28+
import org.apache.hadoop.hdfs.qjournal.protocol.InterQJournalProtocolProtos.GetStorageInfoRequestProto;
2729
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.GetEditLogManifestRequestProto;
2830
import org.apache.hadoop.hdfs.qjournal.protocol.QJournalProtocolProtos.GetEditLogManifestResponseProto;
2931

@@ -60,4 +62,18 @@ public GetEditLogManifestResponseProto getEditLogManifestFromJournal(
6062
throw new ServiceException(e);
6163
}
6264
}
65+
66+
@Override
67+
public StorageInfoProto getStorageInfo(
68+
RpcController controller, GetStorageInfoRequestProto request)
69+
throws ServiceException {
70+
try {
71+
return impl.getStorageInfo(
72+
request.getJid().getIdentifier(),
73+
request.hasNameServiceId() ? request.getNameServiceId() : null
74+
);
75+
} catch (IOException e) {
76+
throw new ServiceException(e);
77+
}
78+
}
6379
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/protocolPB/InterQJournalProtocolTranslatorPB.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
package org.apache.hadoop.hdfs.qjournal.protocolPB;
2121

22+
import org.apache.hadoop.hdfs.protocol.proto.HdfsServerProtos.StorageInfoProto;
23+
import org.apache.hadoop.hdfs.qjournal.protocol.InterQJournalProtocolProtos;
2224
import org.apache.hadoop.thirdparty.protobuf.RpcController;
2325
import org.apache.hadoop.classification.InterfaceAudience;
2426
import org.apache.hadoop.classification.InterfaceStability;
@@ -75,6 +77,18 @@ public GetEditLogManifestResponseProto getEditLogManifestFromJournal(
7577
req.build()));
7678
}
7779

80+
@Override
81+
public StorageInfoProto getStorageInfo(String jid, String nameServiceId)
82+
throws IOException {
83+
InterQJournalProtocolProtos.GetStorageInfoRequestProto.Builder req =
84+
InterQJournalProtocolProtos.GetStorageInfoRequestProto.newBuilder()
85+
.setJid(convertJournalId(jid));
86+
if (nameServiceId != null) {
87+
req.setNameServiceId(nameServiceId);
88+
}
89+
return ipc(() -> rpcProxy.getStorageInfo(NULL_CONTROLLER, req.build()));
90+
}
91+
7892
private QJournalProtocolProtos.JournalIdProto convertJournalId(String jid) {
7993
return QJournalProtocolProtos.JournalIdProto.newBuilder()
8094
.setIdentifier(jid)

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/server/JournalNodeRpcServer.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
package org.apache.hadoop.hdfs.qjournal.server;
1919

2020
import org.apache.hadoop.classification.VisibleForTesting;
21+
import org.apache.hadoop.hdfs.protocol.proto.HdfsServerProtos.StorageInfoProto;
2122
import org.apache.hadoop.thirdparty.protobuf.BlockingService;
2223
import org.slf4j.Logger;
2324
import org.apache.hadoop.classification.InterfaceAudience;
@@ -71,14 +72,14 @@ public class JournalNodeRpcServer implements QJournalProtocol,
7172

7273
JournalNodeRpcServer(Configuration conf, JournalNode jn) throws IOException {
7374
this.jn = jn;
74-
75+
7576
Configuration confCopy = new Configuration(conf);
76-
77+
7778
// Ensure that nagling doesn't kick in, which could cause latency issues.
7879
confCopy.setBoolean(
7980
CommonConfigurationKeysPublic.IPC_SERVER_TCPNODELAY_KEY,
8081
true);
81-
82+
8283
InetSocketAddress addr = getAddress(confCopy);
8384
String bindHost = conf.getTrimmed(DFS_JOURNALNODE_RPC_BIND_HOST_KEY, null);
8485
if (bindHost == null) {
@@ -104,7 +105,7 @@ public class JournalNodeRpcServer implements QJournalProtocol,
104105
this.handlerCount = confHandlerCount;
105106
LOG.info("The number of JournalNodeRpcServer handlers is {}.",
106107
this.handlerCount);
107-
108+
108109
this.server = new RPC.Builder(confCopy)
109110
.setProtocol(QJournalProtocolPB.class)
110111
.setInstance(service)
@@ -149,15 +150,15 @@ void start() {
149150
public InetSocketAddress getAddress() {
150151
return server.getListenerAddress();
151152
}
152-
153+
153154
void join() throws InterruptedException {
154155
this.server.join();
155156
}
156-
157+
157158
void stop() {
158159
this.server.stop();
159160
}
160-
161+
161162
static InetSocketAddress getAddress(Configuration conf) {
162163
String addr = conf.get(
163164
DFSConfigKeys.DFS_JOURNALNODE_RPC_ADDRESS_KEY,
@@ -211,7 +212,7 @@ public void journal(RequestInfo reqInfo,
211212
jn.getOrCreateJournal(reqInfo.getJournalId(), reqInfo.getNameServiceId())
212213
.journal(reqInfo, segmentTxId, firstTxnId, numTxns, records);
213214
}
214-
215+
215216
@Override
216217
public void heartbeat(RequestInfo reqInfo) throws IOException {
217218
jn.getOrCreateJournal(reqInfo.getJournalId(), reqInfo.getNameServiceId())
@@ -245,17 +246,24 @@ public GetEditLogManifestResponseProto getEditLogManifest(
245246
String jid, String nameServiceId,
246247
long sinceTxId, boolean inProgressOk)
247248
throws IOException {
248-
249+
249250
RemoteEditLogManifest manifest = jn.getOrCreateJournal(jid, nameServiceId)
250251
.getEditLogManifest(sinceTxId, inProgressOk);
251-
252+
252253
return GetEditLogManifestResponseProto.newBuilder()
253254
.setManifest(PBHelper.convert(manifest))
254255
.setHttpPort(jn.getBoundHttpAddress().getPort())
255256
.setFromURL(jn.getHttpServerURI())
256257
.build();
257258
}
258259

260+
@Override
261+
public StorageInfoProto getStorageInfo(String jid,
262+
String nameServiceId) throws IOException {
263+
StorageInfo storage = jn.getOrCreateJournal(jid, nameServiceId).getStorage();
264+
return PBHelper.convert(storage);
265+
}
266+
259267
@Override
260268
public GetJournaledEditsResponseProto getJournaledEdits(String jid,
261269
String nameServiceId, long sinceTxId, int maxTxns) throws IOException {

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/qjournal/server/JournalNodeSyncer.java

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@
1818
package org.apache.hadoop.hdfs.qjournal.server;
1919

2020
import org.apache.hadoop.classification.VisibleForTesting;
21+
import org.apache.hadoop.hdfs.protocol.proto.HdfsServerProtos;
22+
import org.apache.hadoop.hdfs.server.common.HdfsServerConstants;
23+
import org.apache.hadoop.hdfs.server.common.StorageInfo;
2124
import org.apache.hadoop.thirdparty.com.google.common.collect.ImmutableList;
2225
import org.apache.hadoop.classification.InterfaceAudience;
2326
import org.apache.hadoop.conf.Configuration;
@@ -79,6 +82,7 @@ public class JournalNodeSyncer {
7982
private int numOtherJNs;
8083
private int journalNodeIndexForSync = 0;
8184
private final long journalSyncInterval;
85+
private final boolean tryFormatting;
8286
private final int logSegmentTransferTimeout;
8387
private final DataTransferThrottler throttler;
8488
private final JournalMetrics metrics;
@@ -98,6 +102,9 @@ public class JournalNodeSyncer {
98102
logSegmentTransferTimeout = conf.getInt(
99103
DFSConfigKeys.DFS_EDIT_LOG_TRANSFER_TIMEOUT_KEY,
100104
DFSConfigKeys.DFS_EDIT_LOG_TRANSFER_TIMEOUT_DEFAULT);
105+
tryFormatting = conf.getBoolean(
106+
DFSConfigKeys.DFS_JOURNALNODE_ENABLE_SYNC_FORMAT_KEY,
107+
DFSConfigKeys.DFS_JOURNALNODE_ENABLE_SYNC_FORMAT_DEFAULT);
101108
throttler = getThrottler(conf);
102109
metrics = journal.getMetrics();
103110
journalSyncerStarted = false;
@@ -171,6 +178,8 @@ private void startSyncJournalsDaemon() {
171178
// Wait for journal to be formatted to create edits.sync directory
172179
while(!journal.isFormatted()) {
173180
try {
181+
// Format the journal with namespace info from the other JNs if it is not formatted
182+
formatWithSyncer();
174183
Thread.sleep(journalSyncInterval);
175184
} catch (InterruptedException e) {
176185
LOG.error("JournalNodeSyncer daemon received Runtime exception.", e);
@@ -187,7 +196,15 @@ private void startSyncJournalsDaemon() {
187196
while(shouldSync) {
188197
try {
189198
if (!journal.isFormatted()) {
190-
LOG.warn("Journal cannot sync. Not formatted.");
199+
LOG.warn("Journal cannot sync. Not formatted. Trying to format with the syncer");
200+
formatWithSyncer();
201+
if (journal.isFormatted() && !createEditsSyncDir()) {
202+
LOG.error("Failed to create directory for downloading log " +
203+
"segments: {}. Stopping Journal Node Sync.",
204+
journal.getStorage().getEditsSyncDir());
205+
return;
206+
}
207+
continue;
191208
} else {
192209
syncJournals();
193210
}
@@ -233,6 +250,68 @@ private void syncJournals() {
233250
journalNodeIndexForSync = (journalNodeIndexForSync + 1) % numOtherJNs;
234251
}
235252

253+
private void formatWithSyncer() {
254+
if (!tryFormatting) {
255+
return;
256+
}
257+
LOG.info("Trying to format the journal with the syncer");
258+
try {
259+
StorageInfo storage = null;
260+
for (JournalNodeProxy jnProxy : otherJNProxies) {
261+
if (!hasEditLogs(jnProxy)) {
262+
// This avoids a race condition between `hdfs namenode -format` and
263+
// JN syncer by checking if the other JN is not newly formatted.
264+
continue;
265+
}
266+
try {
267+
HdfsServerProtos.StorageInfoProto storageInfoResponse =
268+
jnProxy.jnProxy.getStorageInfo(jid, nameServiceId);
269+
storage = PBHelper.convert(
270+
storageInfoResponse, HdfsServerConstants.NodeType.JOURNAL_NODE
271+
);
272+
if (storage.getNamespaceID() == 0) {
273+
LOG.error("Got invalid StorageInfo from " + jnProxy);
274+
storage = null;
275+
continue;
276+
}
277+
LOG.info("Got StorageInfo " + storage + " from " + jnProxy);
278+
break;
279+
} catch (IOException e) {
280+
LOG.error("Could not get StorageInfo from " + jnProxy, e);
281+
}
282+
}
283+
if (storage == null) {
284+
LOG.error("Could not get StorageInfo from any JournalNode. " +
285+
"JournalNodeSyncer cannot format the journal.");
286+
return;
287+
}
288+
NamespaceInfo nsInfo = new NamespaceInfo(storage);
289+
journal.format(nsInfo, true);
290+
} catch (IOException e) {
291+
LOG.error("Exception in formatting the journal with the syncer", e);
292+
}
293+
}
294+
295+
private boolean hasEditLogs(JournalNodeProxy journalProxy) {
296+
GetEditLogManifestResponseProto editLogManifest;
297+
try {
298+
editLogManifest = journalProxy.jnProxy.getEditLogManifestFromJournal(
299+
jid, nameServiceId, 0, false);
300+
} catch (IOException e) {
301+
LOG.error("Could not get edit log manifest from " + journalProxy, e);
302+
return false;
303+
}
304+
305+
List<RemoteEditLog> otherJournalEditLogs = PBHelper.convert(
306+
editLogManifest.getManifest()).getLogs();
307+
if (otherJournalEditLogs == null || otherJournalEditLogs.isEmpty()) {
308+
LOG.warn("Journal at " + journalProxy.jnAddr + " has no edit logs");
309+
return false;
310+
}
311+
312+
return true;
313+
}
314+
236315
private void syncWithJournalAtIndex(int index) {
237316
LOG.info("Syncing Journal " + jn.getBoundIpcAddress().getAddress() + ":"
238317
+ jn.getBoundIpcAddress().getPort() + " with "

hadoop-hdfs-project/hadoop-hdfs/src/main/proto/InterQJournalProtocol.proto

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,15 @@ package hadoop.hdfs.qjournal;
3131
import "HdfsServer.proto";
3232
import "QJournalProtocol.proto";
3333

34+
message GetStorageInfoRequestProto {
35+
required JournalIdProto jid = 1;
36+
optional string nameServiceId = 2;
37+
}
3438

3539
service InterQJournalProtocolService {
3640
rpc getEditLogManifestFromJournal(GetEditLogManifestRequestProto)
3741
returns (GetEditLogManifestResponseProto);
42+
43+
rpc getStorageInfo(GetStorageInfoRequestProto)
44+
returns (StorageInfoProto);
3845
}

hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5071,6 +5071,16 @@
50715071
</description>
50725072
</property>
50735073

5074+
<property>
5075+
<name>dfs.journalnode.enable.sync.format</name>
5076+
<value>false</value>
5077+
<description>
5078+
If true, the journal node syncer daemon that tries to sync edit
5079+
logs between journal nodes will try to format its journal if it is not.
5080+
It will query the other journal nodes for the storage info required to format.
5081+
</description>
5082+
</property>
5083+
50745084
<property>
50755085
<name>dfs.journalnode.edit-cache-size.bytes</name>
50765086
<value></value>

0 commit comments

Comments
 (0)