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:
parent
448ffb12ec
commit
ea25f4de23
@ -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(
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
@ -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);
|
||||||
|
Loading…
Reference in New Issue
Block a user