Skip to content

Commit b05c0ce

Browse files
HDFS-17496. DataNode supports more fine-grained dataset lock based on blockid. (#7280). Contributed by hfutatzhanghb.
Signed-off-by: He Xiaoqiao <[email protected]>
1 parent a787146 commit b05c0ce

File tree

9 files changed

+247
-50
lines changed

9 files changed

+247
-50
lines changed

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/DataNodeLockManager.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,17 @@
2424
public interface DataNodeLockManager<T extends AutoCloseDataSetLock> {
2525

2626
/**
27-
* Acquire block pool level first if you want to Acquire volume lock.
27+
* Acquire block pool level and volume level lock first if you want to acquire dir lock.
2828
* Or only acquire block pool level lock.
29+
* There are several locking sequential patterns as below:
30+
* 1. block pool
31+
* 2. block poll -> volume
32+
* 3. block pool level -> volume -> dir
2933
*/
3034
enum LockLevel {
3135
BLOCK_POOl,
32-
VOLUME
36+
VOLUME,
37+
DIR
3338
}
3439

3540
/**
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.hdfs.server.datanode;
20+
21+
import java.util.List;
22+
23+
public class DataNodeLayoutSubLockStrategy implements DataSetSubLockStrategy {
24+
@Override
25+
public String blockIdToSubLock(long blockid) {
26+
return DatanodeUtil.idToBlockDirSuffix(blockid);
27+
}
28+
29+
@Override
30+
public List<String> getAllSubLockNames() {
31+
return DatanodeUtil.getAllSubDirNameForDataSetLock();
32+
}
33+
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DataSetLockManager.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,13 @@ private String generateLockName(LockLevel level, String... resources) {
9696
+ resources[0] + "volume lock :" + resources[1]);
9797
}
9898
return resources[0] + resources[1];
99+
} else if (resources.length == 3 && level == LockLevel.DIR) {
100+
if (resources[0] == null || resources[1] == null || resources[2] == null) {
101+
throw new IllegalArgumentException("acquire a null dataset lock : "
102+
+ resources[0] + ",volume lock :" + resources[1]
103+
+ ",subdir lock :" + resources[2]);
104+
}
105+
return resources[0] + resources[1] + resources[2];
99106
} else {
100107
throw new IllegalArgumentException("lock level do not match resource");
101108
}
@@ -156,7 +163,7 @@ public DataSetLockManager(Configuration conf, DataNode dn) {
156163
public AutoCloseDataSetLock readLock(LockLevel level, String... resources) {
157164
if (level == LockLevel.BLOCK_POOl) {
158165
return getReadLock(level, resources[0]);
159-
} else {
166+
} else if (level == LockLevel.VOLUME){
160167
AutoCloseDataSetLock bpLock = getReadLock(LockLevel.BLOCK_POOl, resources[0]);
161168
AutoCloseDataSetLock volLock = getReadLock(level, resources);
162169
volLock.setParentLock(bpLock);
@@ -165,14 +172,25 @@ public AutoCloseDataSetLock readLock(LockLevel level, String... resources) {
165172
resources[0]);
166173
}
167174
return volLock;
175+
} else {
176+
AutoCloseDataSetLock bpLock = getReadLock(LockLevel.BLOCK_POOl, resources[0]);
177+
AutoCloseDataSetLock volLock = getReadLock(LockLevel.VOLUME, resources[0], resources[1]);
178+
volLock.setParentLock(bpLock);
179+
AutoCloseDataSetLock dirLock = getReadLock(level, resources);
180+
dirLock.setParentLock(volLock);
181+
if (openLockTrace) {
182+
LOG.debug("Sub lock " + resources[0] + resources[1] + resources[2] + " parent lock " +
183+
resources[0] + resources[1]);
184+
}
185+
return dirLock;
168186
}
169187
}
170188

171189
@Override
172190
public AutoCloseDataSetLock writeLock(LockLevel level, String... resources) {
173191
if (level == LockLevel.BLOCK_POOl) {
174192
return getWriteLock(level, resources[0]);
175-
} else {
193+
} else if (level == LockLevel.VOLUME) {
176194
AutoCloseDataSetLock bpLock = getReadLock(LockLevel.BLOCK_POOl, resources[0]);
177195
AutoCloseDataSetLock volLock = getWriteLock(level, resources);
178196
volLock.setParentLock(bpLock);
@@ -181,6 +199,17 @@ public AutoCloseDataSetLock writeLock(LockLevel level, String... resources) {
181199
resources[0]);
182200
}
183201
return volLock;
202+
} else {
203+
AutoCloseDataSetLock bpLock = getReadLock(LockLevel.BLOCK_POOl, resources[0]);
204+
AutoCloseDataSetLock volLock = getReadLock(LockLevel.VOLUME, resources[0], resources[1]);
205+
volLock.setParentLock(bpLock);
206+
AutoCloseDataSetLock dirLock = getWriteLock(level, resources);
207+
dirLock.setParentLock(volLock);
208+
if (openLockTrace) {
209+
LOG.debug("Sub lock " + resources[0] + resources[1] + resources[2] + " parent lock " +
210+
resources[0] + resources[1]);
211+
}
212+
return dirLock;
184213
}
185214
}
186215

@@ -235,8 +264,13 @@ public void addLock(LockLevel level, String... resources) {
235264
String lockName = generateLockName(level, resources);
236265
if (level == LockLevel.BLOCK_POOl) {
237266
lockMap.addLock(lockName, new ReentrantReadWriteLock(isFair));
267+
} else if (level == LockLevel.VOLUME) {
268+
lockMap.addLock(resources[0], new ReentrantReadWriteLock(isFair));
269+
lockMap.addLock(lockName, new ReentrantReadWriteLock(isFair));
238270
} else {
239271
lockMap.addLock(resources[0], new ReentrantReadWriteLock(isFair));
272+
lockMap.addLock(generateLockName(LockLevel.VOLUME, resources[0], resources[1]),
273+
new ReentrantReadWriteLock(isFair));
240274
lockMap.addLock(lockName, new ReentrantReadWriteLock(isFair));
241275
}
242276
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.hdfs.server.datanode;
20+
21+
import java.util.List;
22+
23+
/**
24+
* This interface is used to generate sub lock name for a blockid.
25+
*/
26+
public interface DataSetSubLockStrategy {
27+
28+
/**
29+
* Generate sub lock name for the given blockid.
30+
* @param blockid the block id.
31+
* @return sub lock name for the input blockid.
32+
*/
33+
String blockIdToSubLock(long blockid);
34+
35+
List<String> getAllSubLockNames();
36+
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/datanode/DatanodeUtil.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.io.FileInputStream;
2222
import java.io.FileNotFoundException;
2323
import java.io.IOException;
24+
import java.util.ArrayList;
25+
import java.util.List;
2426

2527
import org.apache.hadoop.classification.InterfaceAudience;
2628
import org.apache.hadoop.hdfs.protocol.Block;
@@ -37,6 +39,7 @@ public class DatanodeUtil {
3739
public static final String DISK_ERROR = "Possible disk error: ";
3840

3941
private static final String SEP = System.getProperty("file.separator");
42+
private static final long MASK = 0x1F;
4043

4144
/** Get the cause of an I/O exception if caused by a possible disk error
4245
* @param ioe an I/O exception
@@ -112,6 +115,21 @@ public static boolean dirNoFilesRecursive(
112115
return true;
113116
}
114117

118+
/**
119+
* Take an example.
120+
* We hava a block with blockid mapping to:
121+
* "/data1/hadoop/hdfs/datanode/current/BP-xxxx/current/finalized/subdir0/subdir1"
122+
* We return "subdir0/subdir0".
123+
* @param blockId the block id.
124+
* @return two-level subdir string where block will be stored.
125+
*/
126+
public static String idToBlockDirSuffix(long blockId) {
127+
int d1 = (int) ((blockId >> 16) & MASK);
128+
int d2 = (int) ((blockId >> 8) & MASK);
129+
return DataStorage.BLOCK_SUBDIR_PREFIX + d1 + SEP +
130+
DataStorage.BLOCK_SUBDIR_PREFIX + d2;
131+
}
132+
115133
/**
116134
* Get the directory where a finalized block with this ID should be stored.
117135
* Do not attempt to create the directory.
@@ -120,13 +138,21 @@ public static boolean dirNoFilesRecursive(
120138
* @return
121139
*/
122140
public static File idToBlockDir(File root, long blockId) {
123-
int d1 = (int) ((blockId >> 16) & 0x1F);
124-
int d2 = (int) ((blockId >> 8) & 0x1F);
125-
String path = DataStorage.BLOCK_SUBDIR_PREFIX + d1 + SEP +
126-
DataStorage.BLOCK_SUBDIR_PREFIX + d2;
141+
String path = idToBlockDirSuffix(blockId);
127142
return new File(root, path);
128143
}
129144

145+
public static List<String> getAllSubDirNameForDataSetLock() {
146+
List<String> res = new ArrayList<>();
147+
for (int d1 = 0; d1 <= MASK; d1++) {
148+
for (int d2 = 0; d2 <= MASK; d2++) {
149+
res.add(DataStorage.BLOCK_SUBDIR_PREFIX + d1 + SEP +
150+
DataStorage.BLOCK_SUBDIR_PREFIX + d2);
151+
}
152+
}
153+
return res;
154+
}
155+
130156
/**
131157
* @return the FileInputStream for the meta data of the given block.
132158
* @throws FileNotFoundException

0 commit comments

Comments
 (0)