-
Notifications
You must be signed in to change notification settings - Fork 8.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HADOOP-19072. S3A: expand optimisations on stores with "fs.s3a.performance.flags" for mkdir #6543
Changes from all commits
2728303
3874f72
12e6bff
ff8a9d2
8d3012c
fc306d5
b7e4ede
7ce4995
142f0d4
e7ef0d8
c5652a0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,8 @@ | |
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import org.apache.hadoop.classification.InterfaceAudience; | ||
import org.apache.hadoop.classification.InterfaceStability; | ||
import org.apache.hadoop.fs.FileAlreadyExistsException; | ||
import org.apache.hadoop.fs.FileStatus; | ||
import org.apache.hadoop.fs.Path; | ||
|
@@ -54,30 +56,54 @@ | |
* <li>If needed, one PUT</li> | ||
* </ol> | ||
*/ | ||
@InterfaceAudience.Private | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not needed given .impl paackage is tagged privat/unstable |
||
@InterfaceStability.Evolving | ||
public class MkdirOperation extends ExecutingStoreOperation<Boolean> { | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger( | ||
MkdirOperation.class); | ||
|
||
/** | ||
* Path of the directory to be created. | ||
*/ | ||
private final Path dir; | ||
|
||
/** | ||
* Mkdir Callbacks object to be used by the Mkdir operation. | ||
*/ | ||
private final MkdirCallbacks callbacks; | ||
|
||
/** | ||
* Should checks for ancestors existing be skipped? | ||
* This flag is set when working with magic directories. | ||
* Whether to skip the validation of the parent directory. | ||
*/ | ||
private final boolean performanceMkdir; | ||
|
||
/** | ||
* Whether the path is magic commit path. | ||
*/ | ||
private final boolean isMagicPath; | ||
|
||
/** | ||
* Initialize Mkdir Operation context for S3A. | ||
* | ||
* @param storeContext Store context. | ||
* @param dir Dir path of the directory. | ||
* @param callbacks MkdirCallbacks object used by the Mkdir operation. | ||
* @param isMagicPath True if the path is magic commit path. | ||
* @param performanceMkdir If true, skip validation of the parent directory | ||
* structure. | ||
*/ | ||
public MkdirOperation( | ||
final StoreContext storeContext, | ||
final Path dir, | ||
final MkdirCallbacks callbacks, | ||
final boolean isMagicPath) { | ||
final boolean isMagicPath, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. actually, these should be the same flag. so rename it performanceCreation and in s3aFS set to true if the path is magic or There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is from previous patch version. |
||
final boolean performanceMkdir) { | ||
super(storeContext); | ||
this.dir = dir; | ||
this.callbacks = callbacks; | ||
this.isMagicPath = isMagicPath; | ||
this.performanceMkdir = performanceMkdir; | ||
} | ||
|
||
/** | ||
|
@@ -124,7 +150,32 @@ public Boolean execute() throws IOException { | |
return true; | ||
} | ||
|
||
// Walk path to root, ensuring closest ancestor is a directory, not file | ||
// if performance creation mode is set, no need to check | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about on L116 we only do a HEAD check for the path without /, (maybe need new callback), so no LIST probe for a dir via HEAD/LIST
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds reasonable, i was curious about whether we need full probe for magic, i think yes we can make it much performant. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i was wrong; now the patch is in I see where I was mistaken. Its the versioned buckets where problems surface. sorry! |
||
// whether the closest ancestor is dir. | ||
if (!performanceMkdir) { | ||
verifyFileStatusOfClosestAncestor(); | ||
} | ||
|
||
// if we get here there is no directory at the destination. | ||
// so create one. | ||
|
||
// Create the marker file, delete the parent entries | ||
// if the filesystem isn't configured to retain them | ||
callbacks.createFakeDirectory(dir, false); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pass down performanceCreation here; so always keep parent dirs. I know the marker retention default has changed, but we are in perfomance mode here... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is from previous patch version. |
||
return true; | ||
} | ||
|
||
/** | ||
* Verify the file status of the closest ancestor, if it is | ||
* dir, the mkdir operation should proceed. If it is file, | ||
* the mkdir operation should throw error. | ||
* | ||
* @throws IOException If either file status could not be retrieved, | ||
* or if the closest ancestor is a file. | ||
*/ | ||
private void verifyFileStatusOfClosestAncestor() throws IOException { | ||
FileStatus fileStatus; | ||
// Walk path to root, ensuring the closest ancestor is a directory, not file | ||
Path fPart = dir.getParent(); | ||
try { | ||
while (fPart != null && !fPart.isRoot()) { | ||
mukund-thakur marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
@@ -140,24 +191,18 @@ public Boolean execute() throws IOException { | |
} | ||
|
||
// there's a file at the parent entry | ||
throw new FileAlreadyExistsException(String.format( | ||
"Can't make directory for path '%s' since it is a file.", | ||
fPart)); | ||
throw new FileAlreadyExistsException( | ||
String.format( | ||
"Can't make directory for path '%s' since it is a file.", | ||
fPart)); | ||
} | ||
} catch (AccessDeniedException e) { | ||
LOG.info("mkdirs({}}: Access denied when looking" | ||
+ " for parent directory {}; skipping checks", | ||
dir, fPart); | ||
dir, | ||
fPart); | ||
LOG.debug("{}", e, e); | ||
} | ||
|
||
// if we get here there is no directory at the destination. | ||
// so create one. | ||
|
||
// Create the marker file, delete the parent entries | ||
// if the filesystem isn't configured to retain them | ||
callbacks.createFakeDirectory(dir, false); | ||
return true; | ||
} | ||
|
||
/** | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one | ||
* or more contributor license agreements. See the NOTICE file | ||
* distributed with this work for additional information | ||
* regarding copyright ownership. The ASF licenses this file | ||
* to you under the Apache License, Version 2.0 (the | ||
* "License"); you may not use this file except in compliance | ||
* with the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package org.apache.hadoop.fs.contract.s3a; | ||
|
||
import org.junit.Test; | ||
|
||
import org.apache.hadoop.conf.Configuration; | ||
import org.apache.hadoop.fs.FileSystem; | ||
import org.apache.hadoop.fs.Path; | ||
import org.apache.hadoop.fs.contract.AbstractContractMkdirTest; | ||
import org.apache.hadoop.fs.contract.AbstractFSContract; | ||
import org.apache.hadoop.fs.contract.ContractTestUtils; | ||
|
||
import static org.apache.hadoop.fs.contract.ContractTestUtils.createFile; | ||
import static org.apache.hadoop.fs.contract.ContractTestUtils.dataset; | ||
import static org.apache.hadoop.fs.s3a.Constants.FS_S3A_CREATE_PERFORMANCE; | ||
import static org.apache.hadoop.fs.s3a.Constants.FS_S3A_PERFORMANCE_FLAGS; | ||
import static org.apache.hadoop.fs.s3a.S3ATestUtils.removeBaseAndBucketOverrides; | ||
|
||
/** | ||
* Test mkdir operations on S3A with create performance mode. | ||
*/ | ||
public class ITestS3AContractMkdirWithCreatePerf extends AbstractContractMkdirTest { | ||
|
||
@Override | ||
protected Configuration createConfiguration() { | ||
Configuration conf = super.createConfiguration(); | ||
removeBaseAndBucketOverrides( | ||
conf, | ||
FS_S3A_CREATE_PERFORMANCE, | ||
FS_S3A_PERFORMANCE_FLAGS); | ||
conf.setStrings(FS_S3A_PERFORMANCE_FLAGS, | ||
"create,mkdir"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. use just "set" unless you want to provide a list of the enum elements with .toString() after each |
||
return conf; | ||
} | ||
|
||
@Override | ||
protected AbstractFSContract createContract(Configuration conf) { | ||
return new S3AContract(conf); | ||
} | ||
|
||
@Test | ||
public void testMkdirOverParentFile() throws Throwable { | ||
describe("try to mkdir where a parent is a file, should pass"); | ||
FileSystem fs = getFileSystem(); | ||
Path path = methodPath(); | ||
byte[] dataset = dataset(1024, ' ', 'z'); | ||
createFile(getFileSystem(), path, false, dataset); | ||
Path child = new Path(path, "child-to-mkdir"); | ||
boolean childCreated = fs.mkdirs(child); | ||
assertTrue("Child dir is created", childCreated); | ||
assertIsFile(path); | ||
byte[] bytes = ContractTestUtils.readDataset(getFileSystem(), path, dataset.length); | ||
ContractTestUtils.compareByteArrays(dataset, bytes, dataset.length); | ||
assertPathExists("mkdir failed", child); | ||
assertDeleted(child, true); | ||
} | ||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to add numbering in md files.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
doesn't make any difference; IDEs often add them automatically. I personally prefer just
1
because its easier to reorder things, but don't care what others doThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this was done by IDE so i thought of keeping it. earlier i updated this description but now that we have new config, i removed the description here and moved it to
fs.s3a.performance.flags
.