HADOOP-16709. S3Guard: Make authoritative mode exclusive for metadata - don't check for expiry for authoritative paths (#1721). Contributed by Gabor Bota.

This commit is contained in:
Gabor Bota 2019-11-26 16:36:19 +01:00 committed by GitHub
parent 448ffb12ec
commit ea25f4de23
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 150 additions and 84 deletions

View File

@ -2415,9 +2415,10 @@ public FileStatus[] innerListStatus(Path f) throws FileNotFoundException,
key = key + '/'; key = key + '/';
} }
DirListingMetadata dirMeta =
S3Guard.listChildrenWithTtl(metadataStore, path, ttlTimeProvider);
boolean allowAuthoritative = allowAuthoritative(f); boolean allowAuthoritative = allowAuthoritative(f);
DirListingMetadata dirMeta =
S3Guard.listChildrenWithTtl(metadataStore, path, ttlTimeProvider,
allowAuthoritative);
if (allowAuthoritative && dirMeta != null && dirMeta.isAuthoritative()) { if (allowAuthoritative && dirMeta != null && dirMeta.isAuthoritative()) {
return S3Guard.dirMetaToStatuses(dirMeta); return S3Guard.dirMetaToStatuses(dirMeta);
} }
@ -2649,11 +2650,12 @@ S3AFileStatus innerGetFileStatus(final Path f,
String key = pathToKey(path); String key = pathToKey(path);
LOG.debug("Getting path status for {} ({})", path, key); LOG.debug("Getting path status for {} ({})", path, key);
boolean allowAuthoritative = allowAuthoritative(path);
// Check MetadataStore, if any. // Check MetadataStore, if any.
PathMetadata pm = null; PathMetadata pm = null;
if (hasMetadataStore()) { if (hasMetadataStore()) {
pm = S3Guard.getWithTtl(metadataStore, path, ttlTimeProvider, pm = S3Guard.getWithTtl(metadataStore, path, ttlTimeProvider,
needEmptyDirectoryFlag); needEmptyDirectoryFlag, allowAuthoritative);
} }
Set<Path> tombstones = Collections.emptySet(); Set<Path> tombstones = Collections.emptySet();
if (pm != null) { if (pm != null) {
@ -2669,9 +2671,7 @@ S3AFileStatus innerGetFileStatus(final Path f,
// modification - compare the modTime to check if metadata is up to date // modification - compare the modTime to check if metadata is up to date
// Skip going to s3 if the file checked is a directory. Because if the // Skip going to s3 if the file checked is a directory. Because if the
// dest is also a directory, there's no difference. // dest is also a directory, there's no difference.
// TODO After HADOOP-16085 the modification detection can be done with
// etags or object version instead of modTime
boolean allowAuthoritative = allowAuthoritative(path);
if (!pm.getFileStatus().isDirectory() && if (!pm.getFileStatus().isDirectory() &&
!allowAuthoritative && !allowAuthoritative &&
probes.contains(StatusProbeEnum.Head)) { probes.contains(StatusProbeEnum.Head)) {
@ -2709,7 +2709,8 @@ S3AFileStatus innerGetFileStatus(final Path f,
return msStatus; return msStatus;
} else { } else {
DirListingMetadata children = DirListingMetadata children =
S3Guard.listChildrenWithTtl(metadataStore, path, ttlTimeProvider); S3Guard.listChildrenWithTtl(metadataStore, path, ttlTimeProvider,
allowAuthoritative);
if (children != null) { if (children != null) {
tombstones = children.listTombstones(); tombstones = children.listTombstones();
} }
@ -3995,7 +3996,8 @@ private RemoteIterator<S3ALocatedFileStatus> innerListFiles(
cachedFilesIterator = metadataStoreListFilesIterator; cachedFilesIterator = metadataStoreListFilesIterator;
} else { } else {
DirListingMetadata meta = DirListingMetadata meta =
S3Guard.listChildrenWithTtl(metadataStore, path, ttlTimeProvider); S3Guard.listChildrenWithTtl(metadataStore, path, ttlTimeProvider,
allowAuthoritative);
if (meta != null) { if (meta != null) {
tombstones = meta.listTombstones(); tombstones = meta.listTombstones();
} else { } else {
@ -4070,13 +4072,13 @@ public RemoteIterator<LocatedFileStatus> listLocatedStatus(final Path f,
final String key = maybeAddTrailingSlash(pathToKey(path)); final String key = maybeAddTrailingSlash(pathToKey(path));
final Listing.FileStatusAcceptor acceptor = final Listing.FileStatusAcceptor acceptor =
new Listing.AcceptAllButSelfAndS3nDirs(path); new Listing.AcceptAllButSelfAndS3nDirs(path);
boolean allowAuthoritative = allowAuthoritative(f);
DirListingMetadata meta = DirListingMetadata meta =
S3Guard.listChildrenWithTtl(metadataStore, path, S3Guard.listChildrenWithTtl(metadataStore, path,
ttlTimeProvider); ttlTimeProvider, allowAuthoritative);
final RemoteIterator<S3AFileStatus> cachedFileStatusIterator = final RemoteIterator<S3AFileStatus> cachedFileStatusIterator =
listing.createProvidedFileStatusIterator( listing.createProvidedFileStatusIterator(
S3Guard.dirMetaToStatuses(meta), filter, acceptor); S3Guard.dirMetaToStatuses(meta), filter, acceptor);
boolean allowAuthoritative = allowAuthoritative(f);
return (allowAuthoritative && meta != null return (allowAuthoritative && meta != null
&& meta.isAuthoritative()) && meta.isAuthoritative())
? listing.createLocatedFileStatusIterator( ? listing.createLocatedFileStatusIterator(

View File

@ -805,15 +805,9 @@ public DirListingMetadata listChildren(final Path path) throws IOException {
// get a null in DDBPathMetadata. // get a null in DDBPathMetadata.
DDBPathMetadata dirPathMeta = get(path); DDBPathMetadata dirPathMeta = get(path);
// Filter expired entries.
final DirListingMetadata dirListing = final DirListingMetadata dirListing =
getDirListingMetadataFromDirMetaAndList(path, metas, getDirListingMetadataFromDirMetaAndList(path, metas,
dirPathMeta); dirPathMeta);
if(dirListing != null) {
dirListing.removeExpiredEntriesFromListing(
ttlTimeProvider.getMetadataTtl(),
ttlTimeProvider.getNow());
}
return dirListing; return dirListing;
}); });
} }
@ -1001,7 +995,7 @@ public void addAncestors(final Path qualifiedPath,
if (!newDirs.isEmpty()) { if (!newDirs.isEmpty()) {
// patch up the time. // patch up the time.
patchLastUpdated(newDirs, ttlTimeProvider); patchLastUpdated(newDirs, ttlTimeProvider);
innerPut(newDirs, operationState, ttlTimeProvider); innerPut(newDirs, operationState);
} }
} }
@ -1244,7 +1238,7 @@ public void put(
public void put( public void put(
final Collection<? extends PathMetadata> metas, final Collection<? extends PathMetadata> metas,
@Nullable final BulkOperationState operationState) throws IOException { @Nullable final BulkOperationState operationState) throws IOException {
innerPut(pathMetaToDDBPathMeta(metas), operationState, ttlTimeProvider); innerPut(pathMetaToDDBPathMeta(metas), operationState);
} }
/** /**
@ -1258,15 +1252,13 @@ public void put(
* create entries in the table without parents. * create entries in the table without parents.
* @param metas metadata entries to write. * @param metas metadata entries to write.
* @param operationState (nullable) operational state for a bulk update * @param operationState (nullable) operational state for a bulk update
* @param ttlTp The time provider for metadata expiry
* @throws IOException failure. * @throws IOException failure.
*/ */
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter") @SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
@Retries.RetryTranslated @Retries.RetryTranslated
private void innerPut( private void innerPut(
final Collection<DDBPathMetadata> metas, final Collection<DDBPathMetadata> metas,
@Nullable final BulkOperationState operationState, @Nullable final BulkOperationState operationState) throws IOException {
final ITtlTimeProvider ttlTp) throws IOException {
if (metas.isEmpty()) { if (metas.isEmpty()) {
// Happens when someone calls put() with an empty list. // Happens when someone calls put() with an empty list.
LOG.debug("Ignoring empty list of entries to put"); LOG.debug("Ignoring empty list of entries to put");
@ -1641,7 +1633,7 @@ private void removeAuthoritativeDirFlag(
try { try {
LOG.debug("innerPut on metas: {}", metas); LOG.debug("innerPut on metas: {}", metas);
if (!metas.isEmpty()) { if (!metas.isEmpty()) {
innerPut(metas, state, ttlTimeProvider); innerPut(metas, state);
} }
} catch (IOException e) { } catch (IOException e) {
String msg = String.format("IOException while setting false " String msg = String.format("IOException while setting false "
@ -2003,17 +1995,6 @@ public void setTtlTimeProvider(ITtlTimeProvider ttlTimeProvider) {
this.ttlTimeProvider = ttlTimeProvider; this.ttlTimeProvider = ttlTimeProvider;
} }
/**
* Extract a time provider from the argument or fall back to the
* one in the constructor.
* @param ttlTp nullable time source passed in as an argument.
* @return a non-null time source.
*/
private ITtlTimeProvider extractTimeProvider(
@Nullable ITtlTimeProvider ttlTp) {
return ttlTp != null ? ttlTp : this.ttlTimeProvider;
}
/** /**
* Username. * Username.
* @return the current username * @return the current username

View File

@ -213,10 +213,6 @@ public synchronized DirListingMetadata listChildren(Path p) throws
} }
if (listing != null) { if (listing != null) {
listing.removeExpiredEntriesFromListing(
ttlTimeProvider.getMetadataTtl(), ttlTimeProvider.getNow());
LOG.debug("listChildren [after removing expired entries] ({}) -> {}",
path, listing.prettyPrint());
// Make a copy so callers can mutate without affecting our state // Make a copy so callers can mutate without affecting our state
return new DirListingMetadata(listing); return new DirListingMetadata(listing);
} }

View File

@ -712,17 +712,22 @@ static void patchLastUpdated(
/** /**
* Get a path entry provided it is not considered expired. * Get a path entry provided it is not considered expired.
* If the allowAuthoritative flag is true, return without
* checking for TTL expiry.
* @param ms metastore * @param ms metastore
* @param path path to look up. * @param path path to look up.
* @param timeProvider nullable time provider * @param timeProvider nullable time provider
* @param needEmptyDirectoryFlag if true, implementation will * @param needEmptyDirectoryFlag if true, implementation will
* return known state of directory emptiness. * return known state of directory emptiness.
* @param allowAuthoritative if this flag is true, the ttl won't apply to the
* metadata - so it will be returned regardless of it's expiry.
* @return the metadata or null if there as no entry. * @return the metadata or null if there as no entry.
* @throws IOException failure. * @throws IOException failure.
*/ */
public static PathMetadata getWithTtl(MetadataStore ms, Path path, public static PathMetadata getWithTtl(MetadataStore ms, Path path,
@Nullable ITtlTimeProvider timeProvider, @Nullable ITtlTimeProvider timeProvider,
final boolean needEmptyDirectoryFlag) throws IOException { final boolean needEmptyDirectoryFlag,
final boolean allowAuthoritative) throws IOException {
final PathMetadata pathMetadata = ms.get(path, needEmptyDirectoryFlag); final PathMetadata pathMetadata = ms.get(path, needEmptyDirectoryFlag);
// if timeProvider is null let's return with what the ms has // if timeProvider is null let's return with what the ms has
if (timeProvider == null) { if (timeProvider == null) {
@ -730,6 +735,12 @@ public static PathMetadata getWithTtl(MetadataStore ms, Path path,
return pathMetadata; return pathMetadata;
} }
// authoritative mode is enabled for this directory, return what the ms has
if (allowAuthoritative) {
LOG.debug("allowAuthoritative is true, returning pathMetadata as is");
return pathMetadata;
}
long ttl = timeProvider.getMetadataTtl(); long ttl = timeProvider.getMetadataTtl();
if (pathMetadata != null) { if (pathMetadata != null) {
@ -755,15 +766,21 @@ public static PathMetadata getWithTtl(MetadataStore ms, Path path,
/** /**
* List children; mark the result as non-auth if the TTL has expired. * List children; mark the result as non-auth if the TTL has expired.
* If the allowAuthoritative flag is true, return without filtering or
* checking for TTL expiry.
* @param ms metastore * @param ms metastore
* @param path path to look up. * @param path path to look up.
* @param timeProvider nullable time provider * @param timeProvider nullable time provider
* @param allowAuthoritative if this flag is true, the ttl won't apply to the
* metadata - so it will be returned regardless of it's expiry.
* @return the listing of entries under a path, or null if there as no entry. * @return the listing of entries under a path, or null if there as no entry.
* @throws IOException failure. * @throws IOException failure.
*/ */
@Retries.RetryTranslated @RetryTranslated
public static DirListingMetadata listChildrenWithTtl(MetadataStore ms, public static DirListingMetadata listChildrenWithTtl(MetadataStore ms,
Path path, @Nullable ITtlTimeProvider timeProvider) Path path,
@Nullable ITtlTimeProvider timeProvider,
boolean allowAuthoritative)
throws IOException { throws IOException {
DirListingMetadata dlm = ms.listChildren(path); DirListingMetadata dlm = ms.listChildren(path);
@ -772,12 +789,18 @@ public static DirListingMetadata listChildrenWithTtl(MetadataStore ms,
return dlm; return dlm;
} }
long ttl = timeProvider.getMetadataTtl(); if (allowAuthoritative) {
LOG.debug("allowAuthoritative is true, returning pathMetadata as is");
if (dlm != null && dlm.isAuthoritative() return dlm;
&& dlm.isExpired(ttl, timeProvider.getNow())) {
dlm.setAuthoritative(false);
} }
// filter expired entries
if (dlm != null) {
dlm.removeExpiredEntriesFromListing(
timeProvider.getMetadataTtl(),
timeProvider.getNow());
}
return dlm; return dlm;
} }

View File

@ -135,6 +135,8 @@ two different reasons:
* All interactions with the S3 bucket(s) must be through S3A clients sharing * All interactions with the S3 bucket(s) must be through S3A clients sharing
the same metadata store. the same metadata store.
* This is independent from which metadata store implementation is used. * This is independent from which metadata store implementation is used.
* In authoritative mode the metadata TTL metadata expiry is not effective.
This means that the metadata entries won't expire on authoritative paths.
* Authoritative directory listings (isAuthoritative bit) * Authoritative directory listings (isAuthoritative bit)
* Tells if the stored directory listing metadata is complete. * Tells if the stored directory listing metadata is complete.
@ -193,10 +195,14 @@ In particular: **If the Metadata Store is declared as authoritative,
all interactions with the S3 bucket(s) must be through S3A clients sharing all interactions with the S3 bucket(s) must be through S3A clients sharing
the same Metadata Store** the same Metadata Store**
It can be configured how long a directory listing in the MetadataStore is #### TTL metadata expiry
considered as authoritative. If `((lastUpdated + ttl) <= now)` is false, the
directory listing is no longer considered authoritative, so the flag will be It can be configured how long an entry is valid in the MetadataStore
removed on `S3AFileSystem` level. **if the authoritative mode is turned off**, or the path is not
configured to be authoritative.
If `((lastUpdated + ttl) <= now)` is false for an entry, the entry will
be expired, so the S3 bucket will be queried for fresh metadata.
The time for expiry of metadata can be set as the following:
```xml ```xml
<property> <property>

View File

@ -365,16 +365,25 @@ public void testTombstoneExpiryGuardedDeleteRawCreate() throws Exception {
testTimeProvider.getNow(), testTimeProvider.getMetadataTtl()); testTimeProvider.getNow(), testTimeProvider.getMetadataTtl());
// READ GUARDED // READ GUARDED
String newRead = readBytesToString(guardedFs, testFilePath, // This should fail in authoritative mode since we trust the metadatastore
newText.length()); // despite of the expiry. The metadata will not expire.
if (authoritative) {
intercept(FileNotFoundException.class, testFilePath.toString(),
"File should not be present in the metedatastore in authoritative mode.",
() -> readBytesToString(guardedFs, testFilePath, newText.length()));
} else {
String newRead = readBytesToString(guardedFs, testFilePath,
newText.length());
// CHECK LISTING - THE FILE SHOULD BE THERE, TOMBSTONE EXPIRED // CHECK LISTING - THE FILE SHOULD BE THERE, TOMBSTONE EXPIRED
checkListingContainsPath(guardedFs, testFilePath); checkListingContainsPath(guardedFs, testFilePath);
// we can assert that the originalText is the new one, which created raw
LOG.info("Old: {}, New: {}, Read: {}", originalText, newText, newRead);
assertEquals("The text should be modified with a new.", newText,
newRead);
}
// we can assert that the originalText is the new one, which created raw
LOG.info("Old: {}, New: {}, Read: {}", originalText, newText, newRead);
assertEquals("The text should be modified with a new.", newText,
newRead);
} finally { } finally {
guardedFs.delete(testFilePath, true); guardedFs.delete(testFilePath, true);
guardedFs.setTtlTimeProvider(originalTimeProvider); guardedFs.setTtlTimeProvider(originalTimeProvider);
@ -448,8 +457,16 @@ public void testCreateNonRecursiveFailsIfParentDeleted() throws Exception {
// SET TIME SO METADATA EXPIRES // SET TIME SO METADATA EXPIRES
when(mockTimeProvider.getNow()).thenReturn(110L); when(mockTimeProvider.getNow()).thenReturn(110L);
// WRITE TO DELETED DIRECTORY - SUCCESS // WRITE TO DELETED DIRECTORY
createNonRecursive(guardedFs, filePath); // - FAIL ON AUTH = TRUE
// - SUCCESS ON AUTH = FALSE
if (authoritative) {
intercept(FileNotFoundException.class, filePath.getParent().toString(),
"Parent does not exist, so in authoritative mode this should fail.",
() -> createNonRecursive(guardedFs, filePath));
} else {
createNonRecursive(guardedFs, filePath);
}
} finally { } finally {
guardedFs.delete(filePath, true); guardedFs.delete(filePath, true);
@ -546,13 +563,24 @@ public void deleteAfterTombstoneExpiryOobCreate() throws Exception {
when(mockTimeProvider.getNow()).thenReturn(100L + 2 * ttl); when(mockTimeProvider.getNow()).thenReturn(100L + 2 * ttl);
// DELETE IN GUARDED FS // DELETE IN GUARDED FS
// NOTE: in auth this will be ineffective:
// we already have the tombstone marker on the item, it won't expire,
// so we don't delete the raw S3 file.
guardedFs.delete(filePath, true); guardedFs.delete(filePath, true);
// FILE MUST NOT EXIST IN RAW // FILE MUST NOT EXIST IN RAW
intercept(FileNotFoundException.class, filePath.toString(), // If authoritative, the file status can be retrieved raw:
"This file should throw FNFE when reading through " // deleting with guarded FS won't do anything because the tombstone
+ "the raw fs, and the guarded fs deleted the file.", // marker won't expire in auth mode.
() -> rawFS.getFileStatus(filePath)); // If not authoritative, we go to the S3 bucket and get an FNFE
if (authoritative) {
rawFS.getFileStatus(filePath);
} else {
intercept(FileNotFoundException.class, filePath.toString(),
"This file should throw FNFE when reading through "
+ "the raw fs, and the guarded fs deleted the file.",
() -> rawFS.getFileStatus(filePath));
}
} finally { } finally {
guardedFs.delete(filePath, true); guardedFs.delete(filePath, true);
@ -592,8 +620,13 @@ public void testRootTombstones() throws Exception {
checkListingDoesNotContainPath(guardedFs, testFile); checkListingDoesNotContainPath(guardedFs, testFile);
// the tombstone is expired, so we should detect the file // the tombstone is expired, so we should detect the file
// in non-authoritative mode
when(mockTimeProvider.getNow()).thenReturn(100 + ttl); when(mockTimeProvider.getNow()).thenReturn(100 + ttl);
checkListingContainsPath(guardedFs, testFile); if (authoritative) {
checkListingDoesNotContainPath(guardedFs, testFile);
} else {
checkListingContainsPath(guardedFs, testFile);
}
} finally { } finally {
// cleanup // cleanup
guardedFs.delete(base, true); guardedFs.delete(base, true);

View File

@ -59,7 +59,7 @@ public class ITestS3GuardTtl extends AbstractS3ATestBase {
* Test array for parameterized test runs. * Test array for parameterized test runs.
* @return a list of parameter tuples. * @return a list of parameter tuples.
*/ */
@Parameterized.Parameters @Parameterized.Parameters(name = "auth={0}")
public static Collection<Object[]> params() { public static Collection<Object[]> params() {
return Arrays.asList(new Object[][]{ return Arrays.asList(new Object[][]{
{true}, {false} {true}, {false}
@ -133,21 +133,30 @@ public void testDirectoryListingAuthoritativeTtl() throws Exception {
// get an authoritative listing in ms // get an authoritative listing in ms
fs.listStatus(dir); fs.listStatus(dir);
// check if authoritative // check if authoritative
DirListingMetadata dirListing = DirListingMetadata dirListing =
S3Guard.listChildrenWithTtl(ms, dir, mockTimeProvider); S3Guard.listChildrenWithTtl(ms, dir, mockTimeProvider, authoritative);
assertTrue("Listing should be authoritative.", assertTrue("Listing should be authoritative.",
dirListing.isAuthoritative()); dirListing.isAuthoritative());
// change the time, and assume it's not authoritative anymore // change the time, and assume it's not authoritative anymore
// if the metadatastore is not authoritative.
when(mockTimeProvider.getNow()).thenReturn(102L); when(mockTimeProvider.getNow()).thenReturn(102L);
dirListing = S3Guard.listChildrenWithTtl(ms, dir, mockTimeProvider); dirListing = S3Guard.listChildrenWithTtl(ms, dir, mockTimeProvider,
assertFalse("Listing should not be authoritative.", authoritative);
dirListing.isAuthoritative()); if (authoritative) {
assertTrue("Listing should be authoritative.",
dirListing.isAuthoritative());
} else {
assertFalse("Listing should not be authoritative.",
dirListing.isAuthoritative());
}
// get an authoritative listing in ms again - retain test // get an authoritative listing in ms again - retain test
fs.listStatus(dir); fs.listStatus(dir);
// check if authoritative // check if authoritative
dirListing = S3Guard.listChildrenWithTtl(ms, dir, mockTimeProvider); dirListing = S3Guard.listChildrenWithTtl(ms, dir, mockTimeProvider,
authoritative);
assertTrue("Listing shoud be authoritative after listStatus.", assertTrue("Listing shoud be authoritative after listStatus.",
dirListing.isAuthoritative()); dirListing.isAuthoritative());
} finally { } finally {
@ -189,16 +198,24 @@ public void testFileMetadataExpiresTtl() throws Exception {
when(mockTimeProvider.getNow()).thenReturn(110L); when(mockTimeProvider.getNow()).thenReturn(110L);
// metadata is expired so this should refresh the metadata with // metadata is expired so this should refresh the metadata with
// last_updated to the getNow() // last_updated to the getNow() if the store is not authoritative
final FileStatus fileExpire1Status = fs.getFileStatus(fileExpire1); final FileStatus fileExpire1Status = fs.getFileStatus(fileExpire1);
assertNotNull(fileExpire1Status); assertNotNull(fileExpire1Status);
assertEquals(110L, ms.get(fileExpire1).getLastUpdated()); if (authoritative) {
assertEquals(100L, ms.get(fileExpire1).getLastUpdated());
} else {
assertEquals(110L, ms.get(fileExpire1).getLastUpdated());
}
// metadata is expired so this should refresh the metadata with // metadata is expired so this should refresh the metadata with
// last_updated to the getNow() // last_updated to the getNow() if the store is not authoritative
final FileStatus fileExpire2Status = fs.getFileStatus(fileExpire2); final FileStatus fileExpire2Status = fs.getFileStatus(fileExpire2);
assertNotNull(fileExpire2Status); assertNotNull(fileExpire2Status);
assertEquals(110L, ms.get(fileExpire2).getLastUpdated()); if (authoritative) {
assertEquals(101L, ms.get(fileExpire2).getLastUpdated());
} else {
assertEquals(110L, ms.get(fileExpire2).getLastUpdated());
}
final FileStatus fileRetainStatus = fs.getFileStatus(fileRetain); final FileStatus fileRetainStatus = fs.getFileStatus(fileRetain);
assertEquals("Modification time of these files should be equal.", assertEquals("Modification time of these files should be equal.",
@ -347,17 +364,25 @@ public void testListingFilteredExpiredItems() throws Exception {
.hasSize(11) .hasSize(11)
.contains(tombstonedPath); .contains(tombstonedPath);
// listing will be filtered, and won't contain the tombstone with oldtime // listing will be filtered if the store is not authoritative,
// and won't contain the tombstone with oldtime
when(mockTimeProvider.getNow()).thenReturn(newTime); when(mockTimeProvider.getNow()).thenReturn(newTime);
final DirListingMetadata filteredDLM = getDirListingMetadata(ms, final DirListingMetadata filteredDLM = S3Guard.listChildrenWithTtl(
baseDirPath); ms, baseDirPath, mockTimeProvider, authoritative);
containedPaths = filteredDLM.getListing().stream() containedPaths = filteredDLM.getListing().stream()
.map(pm -> pm.getFileStatus().getPath()) .map(pm -> pm.getFileStatus().getPath())
.collect(Collectors.toList()); .collect(Collectors.toList());
Assertions.assertThat(containedPaths) if (authoritative) {
.describedAs("Full listing of path %s", baseDirPath) Assertions.assertThat(containedPaths)
.hasSize(10) .describedAs("Full listing of path %s", baseDirPath)
.doesNotContain(tombstonedPath); .hasSize(11)
.contains(tombstonedPath);
} else {
Assertions.assertThat(containedPaths)
.describedAs("Full listing of path %s", baseDirPath)
.hasSize(10)
.doesNotContain(tombstonedPath);
}
} finally { } finally {
fs.delete(baseDirPath, true); fs.delete(baseDirPath, true);
fs.setTtlTimeProvider(originalTimeProvider); fs.setTtlTimeProvider(originalTimeProvider);

View File

@ -168,7 +168,7 @@ public void testGetWithTtlExpired() throws Exception {
// act // act
final PathMetadata pmExpired = S3Guard.getWithTtl(ms, path, timeProvider, final PathMetadata pmExpired = S3Guard.getWithTtl(ms, path, timeProvider,
false); false, false);
// assert // assert
assertNull(pmExpired); assertNull(pmExpired);
@ -193,7 +193,7 @@ public void testGetWithTtlNotExpired() throws Exception {
// act // act
final PathMetadata pmNotExpired = final PathMetadata pmNotExpired =
S3Guard.getWithTtl(ms, path, timeProvider, false); S3Guard.getWithTtl(ms, path, timeProvider, false, false);
// assert // assert
assertNotNull(pmNotExpired); assertNotNull(pmNotExpired);
@ -220,7 +220,7 @@ public void testGetWithZeroLastUpdatedNotExpired() throws Exception {
// act // act
final PathMetadata pmExpired = S3Guard.getWithTtl(ms, path, timeProvider, final PathMetadata pmExpired = S3Guard.getWithTtl(ms, path, timeProvider,
false); false, false);
// assert // assert
assertNotNull(pmExpired); assertNotNull(pmExpired);