Skip to content

Commit 10ce000

Browse files
smengclShashikant Banerjee
authored andcommitted
HDFS-15492. Make trash root inside each snapshottable directory (apache#2176)
Conflicts: hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DistributedFileSystem.java hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/protocolPB/PBHelperClient.java hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/TestDistributedFileSystem.java Change-Id: Ibe23d5760d61326865dad93460c6b426d002947d
1 parent b6e7a13 commit 10ce000

File tree

8 files changed

+467
-14
lines changed

8 files changed

+467
-14
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FsServerDefaults.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public Writable newInstance() {
5656
private DataChecksum.Type checksumType;
5757
private String keyProviderUri;
5858
private byte storagepolicyId;
59+
private boolean snapshotTrashRootEnabled;
5960

6061
public FsServerDefaults() {
6162
}
@@ -83,6 +84,18 @@ public FsServerDefaults(long blockSize, int bytesPerChecksum,
8384
boolean encryptDataTransfer, long trashInterval,
8485
DataChecksum.Type checksumType,
8586
String keyProviderUri, byte storagepolicy) {
87+
this(blockSize, bytesPerChecksum, writePacketSize, replication,
88+
fileBufferSize, encryptDataTransfer, trashInterval,
89+
checksumType, keyProviderUri, storagepolicy,
90+
false);
91+
}
92+
93+
public FsServerDefaults(long blockSize, int bytesPerChecksum,
94+
int writePacketSize, short replication, int fileBufferSize,
95+
boolean encryptDataTransfer, long trashInterval,
96+
DataChecksum.Type checksumType,
97+
String keyProviderUri, byte storagepolicy,
98+
boolean snapshotTrashRootEnabled) {
8699
this.blockSize = blockSize;
87100
this.bytesPerChecksum = bytesPerChecksum;
88101
this.writePacketSize = writePacketSize;
@@ -93,6 +106,7 @@ public FsServerDefaults(long blockSize, int bytesPerChecksum,
93106
this.checksumType = checksumType;
94107
this.keyProviderUri = keyProviderUri;
95108
this.storagepolicyId = storagepolicy;
109+
this.snapshotTrashRootEnabled = snapshotTrashRootEnabled;
96110
}
97111

98112
public long getBlockSize() {
@@ -139,6 +153,10 @@ public byte getDefaultStoragePolicyId() {
139153
return storagepolicyId;
140154
}
141155

156+
public boolean getSnapshotTrashRootEnabled() {
157+
return snapshotTrashRootEnabled;
158+
}
159+
142160
// /////////////////////////////////////////
143161
// Writable
144162
// /////////////////////////////////////////

hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSClient.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3098,6 +3098,32 @@ boolean isHDFSEncryptionEnabled() throws IOException {
30983098
return getKeyProviderUri() != null;
30993099
}
31003100

3101+
boolean isSnapshotTrashRootEnabled() throws IOException {
3102+
return getServerDefaults().getSnapshotTrashRootEnabled();
3103+
}
3104+
3105+
/**
3106+
* Get the snapshot root of a given file or directory if it exists.
3107+
* e.g. if /snapdir1 is a snapshottable directory and path given is
3108+
* /snapdir1/path/to/file, this method would return /snapdir1
3109+
* @param path Path to a file or a directory.
3110+
* @return Not null if found in a snapshot root directory.
3111+
* @throws IOException
3112+
*/
3113+
String getSnapshotRoot(Path path) throws IOException {
3114+
SnapshottableDirectoryStatus[] dirStatusList = getSnapshottableDirListing();
3115+
if (dirStatusList == null) {
3116+
return null;
3117+
}
3118+
for (SnapshottableDirectoryStatus dirStatus : dirStatusList) {
3119+
String currDir = dirStatus.getFullPath().toString();
3120+
if (path.toUri().getPath().startsWith(currDir)) {
3121+
return currDir;
3122+
}
3123+
}
3124+
return null;
3125+
}
3126+
31013127
/**
31023128
* Returns the SaslDataTransferClient configured for this DFSClient.
31033129
*

hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DFSUtilClient.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -957,4 +957,16 @@ public static String getEZTrashRoot(EncryptionZone ez,
957957
return (ezpath.equals("/") ? ezpath : ezpath + Path.SEPARATOR)
958958
+ FileSystem.TRASH_PREFIX + Path.SEPARATOR + ugi.getShortUserName();
959959
}
960+
961+
/**
962+
* Returns trash root in a snapshottable directory.
963+
* @param ssRoot String of path to a snapshottable directory root.
964+
* @param ugi user of trash owner.
965+
* @return unqualified path of trash root.
966+
*/
967+
public static String getSnapshotTrashRoot(String ssRoot,
968+
UserGroupInformation ugi) {
969+
return (ssRoot.equals("/") ? ssRoot : ssRoot + Path.SEPARATOR)
970+
+ FileSystem.TRASH_PREFIX + Path.SEPARATOR + ugi.getShortUserName();
971+
}
960972
}

hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DistributedFileSystem.java

Lines changed: 100 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,12 @@
125125
import java.util.Arrays;
126126
import java.util.Collection;
127127
import java.util.EnumSet;
128+
import java.util.HashSet;
128129
import java.util.List;
129130
import java.util.Map;
130131
import java.util.NoSuchElementException;
131132
import java.util.Optional;
133+
import java.util.Set;
132134

133135
/****************************************************************
134136
* Implementation of the abstract FileSystem for the DFS system.
@@ -3213,49 +3215,102 @@ public Void next(final FileSystem fs, final Path p) throws IOException {
32133215
/**
32143216
* Get the root directory of Trash for a path in HDFS.
32153217
* 1. File in encryption zone returns /ez1/.Trash/username
3216-
* 2. File not in encryption zone, or encountered exception when checking
3217-
* the encryption zone of the path, returns /users/username/.Trash
3218+
* 2. File in snapshottable directory returns /snapdir1/.Trash/username
3219+
* if dfs.namenode.snapshot.trashroot.enabled is set to true.
3220+
* 3. In other cases, or encountered exception when checking the encryption
3221+
* zone or when checking snapshot root of the path, returns
3222+
* /users/username/.Trash
32183223
* Caller appends either Current or checkpoint timestamp for trash destination
32193224
* @param path the trash root of the path to be determined.
32203225
* @return trash root
32213226
*/
32223227
@Override
32233228
public Path getTrashRoot(Path path) {
3229+
statistics.incrementReadOps(1);
3230+
storageStatistics.incrementOpCounter(OpType.GET_TRASH_ROOT);
3231+
if (path == null) {
3232+
return super.getTrashRoot(null);
3233+
}
3234+
3235+
// Snapshottable directory trash root, not null if path is inside a
3236+
// snapshottable directory and isSnapshotTrashRootEnabled is true from NN.
3237+
String ssTrashRoot = null;
32243238
try {
3225-
if ((path == null) || !dfs.isHDFSEncryptionEnabled()) {
3226-
return super.getTrashRoot(path);
3239+
if (dfs.isSnapshotTrashRootEnabled()) {
3240+
String ssRoot = dfs.getSnapshotRoot(path);
3241+
if (ssRoot != null) {
3242+
ssTrashRoot = DFSUtilClient.getSnapshotTrashRoot(ssRoot, dfs.ugi);
3243+
}
3244+
}
3245+
} catch (IOException ioe) {
3246+
DFSClient.LOG.warn("Exception while checking whether the path is in a "
3247+
+ "snapshottable directory", ioe);
3248+
}
3249+
3250+
try {
3251+
if (!dfs.isHDFSEncryptionEnabled()) {
3252+
if (ssTrashRoot == null) {
3253+
// the path is not in a snapshottable directory and EZ is not enabled
3254+
return super.getTrashRoot(path);
3255+
} else {
3256+
return this.makeQualified(new Path(ssTrashRoot));
3257+
}
32273258
}
32283259
} catch (IOException ioe) {
32293260
DFSClient.LOG.warn("Exception while checking whether encryption zone is "
32303261
+ "supported", ioe);
32313262
}
32323263

3233-
String parentSrc = path.isRoot()?
3234-
path.toUri().getPath():path.getParent().toUri().getPath();
3264+
// HDFS encryption is enabled on the cluster at this point, does not
3265+
// necessary mean the given path is in an EZ hence the check.
3266+
String parentSrc = path.isRoot() ?
3267+
path.toUri().getPath() : path.getParent().toUri().getPath();
3268+
String ezTrashRoot = null;
32353269
try {
32363270
EncryptionZone ez = dfs.getEZForPath(parentSrc);
32373271
if ((ez != null)) {
3238-
return this.makeQualified(
3239-
new Path(DFSUtilClient.getEZTrashRoot(ez, dfs.ugi)));
3272+
ezTrashRoot = DFSUtilClient.getEZTrashRoot(ez, dfs.ugi);
32403273
}
32413274
} catch (IOException e) {
32423275
DFSClient.LOG.warn("Exception in checking the encryption zone for the " +
32433276
"path " + parentSrc + ". " + e.getMessage());
32443277
}
3245-
return super.getTrashRoot(path);
3278+
3279+
if (ssTrashRoot == null) {
3280+
if (ezTrashRoot == null) {
3281+
// The path is neither in a snapshottable directory nor in an EZ
3282+
return super.getTrashRoot(path);
3283+
} else {
3284+
return this.makeQualified(new Path(ezTrashRoot));
3285+
}
3286+
} else {
3287+
if (ezTrashRoot == null) {
3288+
return this.makeQualified(new Path(ssTrashRoot));
3289+
} else {
3290+
// The path is in EZ and in a snapshottable directory
3291+
return this.makeQualified(new Path(
3292+
ssTrashRoot.length() > ezTrashRoot.length() ?
3293+
ssTrashRoot : ezTrashRoot));
3294+
}
3295+
}
32463296
}
32473297

32483298
/**
32493299
* Get all the trash roots of HDFS for current user or for all the users.
3250-
* 1. File deleted from non-encryption zone /user/username/.Trash
3251-
* 2. File deleted from encryption zones
3300+
* 1. File deleted from encryption zones
32523301
* e.g., ez1 rooted at /ez1 has its trash root at /ez1/.Trash/$USER
3302+
* 2. File deleted from snapshottable directories
3303+
* if dfs.namenode.snapshot.trashroot.enabled is set to true.
3304+
* e.g., snapshottable directory /snapdir1 has its trash root
3305+
* at /snapdir1/.Trash/$USER
3306+
* 3. File deleted from other directories
3307+
* /user/username/.Trash
32533308
* @param allUsers return trashRoots of all users if true, used by emptier
32543309
* @return trash roots of HDFS
32553310
*/
32563311
@Override
32573312
public Collection<FileStatus> getTrashRoots(boolean allUsers) {
3258-
List<FileStatus> ret = new ArrayList<>();
3313+
Set<FileStatus> ret = new HashSet<>();
32593314
// Get normal trash roots
32603315
ret.addAll(super.getTrashRoots(allUsers));
32613316

@@ -3286,6 +3341,39 @@ public Collection<FileStatus> getTrashRoots(boolean allUsers) {
32863341
} catch (IOException e){
32873342
DFSClient.LOG.warn("Cannot get all encrypted trash roots", e);
32883343
}
3344+
3345+
try {
3346+
// Get snapshottable directory trash roots
3347+
if (dfs.isSnapshotTrashRootEnabled()) {
3348+
SnapshottableDirectoryStatus[] lst = dfs.getSnapshottableDirListing();
3349+
if (lst != null) {
3350+
for (SnapshottableDirectoryStatus dirStatus : lst) {
3351+
String ssDir = dirStatus.getFullPath().toString();
3352+
Path ssTrashRoot = new Path(ssDir, FileSystem.TRASH_PREFIX);
3353+
if (!exists(ssTrashRoot)) {
3354+
continue;
3355+
}
3356+
if (allUsers) {
3357+
for (FileStatus candidate : listStatus(ssTrashRoot)) {
3358+
if (exists(candidate.getPath())) {
3359+
ret.add(candidate);
3360+
}
3361+
}
3362+
} else {
3363+
Path userTrash = new Path(DFSUtilClient.getSnapshotTrashRoot(
3364+
ssDir, dfs.ugi));
3365+
try {
3366+
ret.add(getFileStatus(userTrash));
3367+
} catch (FileNotFoundException ignored) {
3368+
}
3369+
}
3370+
}
3371+
}
3372+
}
3373+
} catch (IOException e) {
3374+
DFSClient.LOG.warn("Cannot get snapshot trash roots", e);
3375+
}
3376+
32893377
return ret;
32903378
}
32913379

hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/protocolPB/PBHelperClient.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2148,7 +2148,8 @@ public static FsServerDefaults convert(FsServerDefaultsProto fs) {
21482148
fs.getTrashInterval(),
21492149
convert(fs.getChecksumType()),
21502150
fs.hasKeyProviderUri() ? fs.getKeyProviderUri() : null,
2151-
(byte) fs.getPolicyId());
2151+
(byte) fs.getPolicyId(),
2152+
fs.getSnapshotTrashRootEnabled());
21522153
}
21532154

21542155
public static List<CryptoProtocolVersionProto> convert(
@@ -2324,6 +2325,7 @@ public static FsServerDefaultsProto convert(FsServerDefaults fs) {
23242325
.setChecksumType(convert(fs.getChecksumType()))
23252326
.setKeyProviderUri(fs.getKeyProviderUri())
23262327
.setPolicyId(fs.getDefaultStoragePolicyId())
2328+
.setSnapshotTrashRootEnabled(fs.getSnapshotTrashRootEnabled())
23272329
.build();
23282330
}
23292331

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ message FsServerDefaultsProto {
521521
optional ChecksumTypeProto checksumType = 8 [default = CHECKSUM_CRC32];
522522
optional string keyProviderUri = 9;
523523
optional uint32 policyId = 10 [default = 0];
524+
optional bool snapshotTrashRootEnabled = 11 [default = false];
524525
}
525526

526527

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,6 +371,12 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
371371

372372
public static final org.slf4j.Logger LOG = LoggerFactory
373373
.getLogger(FSNamesystem.class.getName());
374+
375+
// The following are private configurations
376+
static final String DFS_NAMENODE_SNAPSHOT_TRASHROOT_ENABLED =
377+
"dfs.namenode.snapshot.trashroot.enabled";
378+
static final boolean DFS_NAMENODE_SNAPSHOT_TRASHROOT_ENABLED_DEFAULT = false;
379+
374380
private final MetricsRegistry registry = new MetricsRegistry("FSNamesystem");
375381
@Metric final MutableRatesWithAggregation detailedLockHoldTimeMetrics =
376382
registry.newRatesWithAggregation("detailedLockHoldTimeMetrics");
@@ -857,7 +863,9 @@ static FSNamesystem loadFromDisk(Configuration conf) throws IOException {
857863
conf.getTrimmed(
858864
CommonConfigurationKeysPublic.HADOOP_SECURITY_KEY_PROVIDER_PATH,
859865
""),
860-
blockManager.getStoragePolicySuite().getDefaultPolicy().getId());
866+
blockManager.getStoragePolicySuite().getDefaultPolicy().getId(),
867+
conf.getBoolean(DFS_NAMENODE_SNAPSHOT_TRASHROOT_ENABLED,
868+
DFS_NAMENODE_SNAPSHOT_TRASHROOT_ENABLED_DEFAULT));
861869

862870
this.maxFsObjects = conf.getLong(DFS_NAMENODE_MAX_OBJECTS_KEY,
863871
DFS_NAMENODE_MAX_OBJECTS_DEFAULT);

0 commit comments

Comments
 (0)