Skip to content

Commit c3c60ec

Browse files
author
Gabor Bota
committed
HADOOP-16380. Added a test which would be more roboust, but I can't reproduce the failure
1 parent ccaa99c commit c3c60ec

File tree

2 files changed

+110
-0
lines changed

2 files changed

+110
-0
lines changed

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/ITestS3GuardEmptyDirs.java

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,19 @@
1919
package org.apache.hadoop.fs.s3a;
2020

2121
import org.apache.hadoop.fs.Path;
22+
import org.apache.hadoop.fs.s3a.s3guard.DirListingMetadata;
2223
import org.apache.hadoop.fs.s3a.s3guard.MetadataStore;
2324
import org.apache.hadoop.fs.s3a.s3guard.NullMetadataStore;
25+
import org.apache.hadoop.fs.s3a.s3guard.PathMetadata;
2426
import org.junit.Assume;
2527
import org.junit.Test;
2628

29+
import java.util.ArrayList;
30+
import java.util.List;
31+
import java.util.UUID;
32+
2733
import static org.apache.hadoop.fs.contract.ContractTestUtils.touch;
34+
import static org.apache.hadoop.fs.s3a.S3ATestUtils.getStatusWithEmptyDirFlag;
2835

2936
/**
3037
* Test logic around whether or not a directory is empty, with S3Guard enabled.
@@ -82,4 +89,91 @@ public void testEmptyDirs() throws Exception {
8289
configuredMs.forgetMetadata(existingDir);
8390
}
8491
}
92+
93+
@Test
94+
public void testEmptyDirsTombstoneMisleadAboutDirectoryStatus()
95+
throws Exception {
96+
S3AFileSystem fs = getFileSystem();
97+
Assume.assumeTrue(fs.hasMetadataStore());
98+
MetadataStore configuredMs = fs.getMetadataStore();
99+
String parentDir = "existing-dir";
100+
Path existingDir = path(parentDir);
101+
102+
List<Path> rawS3FsCreatedFiles = new ArrayList<>();
103+
for (int i = 0; i < 10; i++) {
104+
rawS3FsCreatedFiles.add(
105+
path(parentDir+"/ZZZZ-"+i+"-rawS3File-"+ UUID.randomUUID())
106+
);
107+
}
108+
109+
List<Path> guardedS3FsCreatedFiles = new ArrayList<>();
110+
for (int i = 0; i < 10; i++) {
111+
guardedS3FsCreatedFiles.add(
112+
path(parentDir+"/AAAA-"+i+"guardedFile-" + UUID.randomUUID())
113+
);
114+
}
115+
116+
try {
117+
// 1. Simulate files already existing in the bucket before we started our
118+
// cluster. Temporarily disable the MetadataStore so it doesn't witness
119+
// us creating these files.
120+
121+
fs.setMetadataStore(new NullMetadataStore());
122+
assertTrue(fs.mkdirs(existingDir));
123+
for (Path existingFile : rawS3FsCreatedFiles) {
124+
touch(fs, existingFile);
125+
}
126+
127+
// 2. Simulate (from MetadataStore's perspective) starting our cluster and
128+
// creating a file in an existing directory.
129+
fs.setMetadataStore(configuredMs); // "start cluster"
130+
131+
for (Path guardedS3FsCreatedFile : guardedS3FsCreatedFiles) {
132+
touch(fs, guardedS3FsCreatedFile);
133+
}
134+
135+
S3AFileStatus status = getStatusWithEmptyDirFlag(fs, existingDir);
136+
assertNonEmptyDir(status);
137+
System.out.println(status);
138+
139+
// 3. Assert that removing the only file the MetadataStore witnessed
140+
// being created doesn't cause it to think the directory is now empty.
141+
for (Path newFile : guardedS3FsCreatedFiles) {
142+
fs.delete(newFile, false);
143+
}
144+
status = getStatusWithEmptyDirFlag(fs, existingDir);
145+
assertEquals("Should be empty dir: " + status,
146+
Tristate.FALSE,
147+
status.isEmptyDirectory());
148+
149+
for (Path newFile : guardedS3FsCreatedFiles) {
150+
PathMetadata newFileMD = configuredMs.get(newFile);
151+
assertNotNull("No metadata entry for " + newFile,
152+
newFileMD);
153+
assertTrue("Not a tombstone: "+ newFileMD,
154+
newFileMD.isDeleted());
155+
}
156+
157+
// 4. Assert that removing the final file, that existed "before"
158+
// MetadataStore started, *does* cause the directory to be marked empty.
159+
for (Path existingFile : rawS3FsCreatedFiles) {
160+
fs.delete(existingFile, false);
161+
}
162+
status = getStatusWithEmptyDirFlag(fs, existingDir);
163+
assertEquals("Should be empty dir now: " + status, Tristate.TRUE,
164+
status.isEmptyDirectory());
165+
166+
} finally {
167+
for (Path existingFile : rawS3FsCreatedFiles) {
168+
configuredMs.forgetMetadata(existingFile);
169+
}
170+
configuredMs.forgetMetadata(existingDir);
171+
fs.setMetadataStore(configuredMs);
172+
}
173+
}
174+
175+
protected void assertNonEmptyDir(final S3AFileStatus status) {
176+
assertEquals("Should not be empty dir: " + status, Tristate.FALSE,
177+
status.isEmptyDirectory());
178+
}
85179
}

hadoop-tools/hadoop-aws/src/test/java/org/apache/hadoop/fs/s3a/S3ATestUtils.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,22 @@ public static <T extends Service> T terminateService(final T service) {
818818
return null;
819819
}
820820

821+
/**
822+
* Get a file status from S3A with the {@code needEmptyDirectoryFlag}
823+
* state probed.
824+
* This accesses a package-private method in the
825+
* S3A filesystem.
826+
* @param fs filesystem
827+
* @param dir directory
828+
* @return a status
829+
* @throws IOException
830+
*/
831+
public static S3AFileStatus getStatusWithEmptyDirFlag(
832+
final S3AFileSystem fs,
833+
final Path dir) throws IOException {
834+
return fs.innerGetFileStatus(dir, true);
835+
}
836+
821837
/**
822838
* Helper class to do diffs of metrics.
823839
*/

0 commit comments

Comments
 (0)