Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
import org.apache.hadoop.fs.FileAlreadyExistsException;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FsShell;
import org.apache.hadoop.fs.LocatedFileStatus;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.PathFilter;
Expand Down Expand Up @@ -403,6 +404,49 @@ public void testCreateKeyWithECReplicationConfig() throws Exception {
createKeyWithECReplicationConfig(root, cluster.getConf());
}

@Test
void testContentSummaryErasureCodingPolicy() throws Exception {
String ratisKey = "ratis-ec-policy-key";
String ecKey = "ec-policy-key";
ECReplicationConfig ecConfig = new ECReplicationConfig("RS-3-2-1024k");
Path parentDir = new Path(OZONE_URI_DELIMITER, "ec-policy-mixed-o3fs");
Path ratisFile = new Path(parentDir, ratisKey);
Path ecFile = new Path(parentDir, ecKey);

fs.mkdirs(parentDir);
String ratisRelKey = "ec-policy-mixed-o3fs/" + ratisKey;
String ecRelKey = "ec-policy-mixed-o3fs/" + ecKey;
TestDataUtil.createKey(ozoneBucket, ratisRelKey,
RatisReplicationConfig.getInstance(HddsProtos.ReplicationFactor.THREE),
new byte[]{0});
TestDataUtil.createKey(ozoneBucket, ecRelKey, ecConfig,
new byte[]{0});

try {
assertEquals("",
fs.getContentSummary(ROOT).getErasureCodingPolicy());
assertEquals("Replicated",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, ContentSummaryComputationContext.REPLICATED is better than "Replicated"

But wouldn't work with Hadoop 2 so this is fine.

fs.getContentSummary(ratisFile).getErasureCodingPolicy());
assertEquals(ecConfig.getReplication(),
fs.getContentSummary(ecFile).getErasureCodingPolicy());
assertEquals("",
fs.getContentSummary(parentDir).getErasureCodingPolicy());
} finally {
fs.delete(parentDir, true);
}
}

@Test
void testLsDashEDoesNotThrow() throws Exception {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Medium — weak regression coverage: testLsDashEDoesNotThrow only checks exit code 0. It does not verify that ls -e prints the expected EC policy values (Replicated, RS-3-2-1024k, etc.).

Consider capturing FsShell output (or asserting on getContentSummary() in the same test setup) so a partial fix that still returns null on some paths would be caught.

Same note applies to AbstractRootedOzoneFileSystemTest.testLsDashEDoesNotThrow (~line 1902).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for checking this. The two tests intentionally cover separate layers:

The CLI test covers the original exception, while testContentSummaryErasureCodingPolicy verifies the exact policy values. I’d prefer to keep them separate.

FsShell shell = new FsShell(fs.getConf());
try {
int exitCode = shell.run(new String[]{"-ls", "-R", "-e", fsRoot});
assertEquals(0, exitCode);
} finally {
shell.close();
}
}

@Test
public void testDeleteCreatesFakeParentDir() throws Exception {
deleteCreatesFakeParentDir(ROOT);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FsShell;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.PathIsNotEmptyDirectoryException;
import org.apache.hadoop.fs.StreamCapabilities;
Expand Down Expand Up @@ -1859,6 +1860,56 @@ void testCreateAndCheckECFileDiskUsage() throws Exception {
fs.delete(filePath, true);
}

@Test
void testContentSummaryErasureCodingPolicy() throws Exception {
String ratisKey = "ratis-ec-policy-key";
String ecKey = "ec-policy-key";
ECReplicationConfig ecConfig = new ECReplicationConfig("RS-3-2-1024k");
Path parentDir = new Path(bucketPath, "ec-policy-mixed");
Path ratisFile = new Path(parentDir, ratisKey);
Path ecFile = new Path(parentDir, ecKey);

fs.mkdirs(parentDir);
OzoneBucket bucket = objectStore.getVolume(volumeName).getBucket(bucketName);
String ratisRelKey = "ec-policy-mixed/" + ratisKey;
String ecRelKey = "ec-policy-mixed/" + ecKey;
TestDataUtil.createKey(bucket, ratisRelKey,
RatisReplicationConfig.getInstance(HddsProtos.ReplicationFactor.THREE),
new byte[]{0});
TestDataUtil.createKey(bucket, ecRelKey, ecConfig,
new byte[]{0});

try {
assertEquals("",
fs.getContentSummary(new Path(OZONE_URI_DELIMITER))
.getErasureCodingPolicy());
assertEquals("",
fs.getContentSummary(volumePath).getErasureCodingPolicy());
assertEquals("",
fs.getContentSummary(bucketPath).getErasureCodingPolicy());
assertEquals("Replicated",
fs.getContentSummary(ratisFile).getErasureCodingPolicy());
assertEquals(ecConfig.getReplication(),
fs.getContentSummary(ecFile).getErasureCodingPolicy());
assertEquals("",
fs.getContentSummary(parentDir).getErasureCodingPolicy());
} finally {
fs.delete(parentDir, true);
}
}

@Test
void testLsDashEDoesNotThrow() throws Exception {
FsShell shell = new FsShell(conf);
try {
int exitCode = shell.run(new String[]{"-ls", "-R", "-e",
OZONE_URI_DELIMITER + volumeName + OZONE_URI_DELIMITER + bucketName});
assertEquals(0, exitCode);
} finally {
shell.close();
}
}

@Test
void testCreateAndCheckRatisFileDiskUsage() throws Exception {
String key = "ratiskeytest";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,13 @@ private FileStatusAdapter toFileStatusAdapter(OzoneFileStatus status,
OmKeyInfo keyInfo = status.getKeyInfo();
short replication = (short) keyInfo.getReplicationConfig()
.getRequiredNodes();
boolean isEc = OzoneClientUtils.isKeyErasureCode(keyInfo);
String ecPolicy;
if (isEc) {
ecPolicy = keyInfo.getReplicationConfig().getReplication();
} else {
ecPolicy = status.isFile() ? "Replicated" : "";
}
return new FileStatusAdapter(
keyInfo.getDataSize(),
keyInfo.getReplicatedSize(),
Expand All @@ -553,7 +560,8 @@ private FileStatusAdapter toFileStatusAdapter(OzoneFileStatus status,
null,
getBlockLocations(status),
OzoneClientUtils.isKeyEncrypted(keyInfo),
OzoneClientUtils.isKeyErasureCode(keyInfo)
isEc,
ecPolicy
);
}

Expand All @@ -562,6 +570,13 @@ private FileStatusAdapter toFileStatusAdapter(OzoneFileStatusLight status,
BasicOmKeyInfo keyInfo = status.getKeyInfo();
short replication = (short) keyInfo.getReplicationConfig()
.getRequiredNodes();
boolean isEc = OzoneClientUtils.isKeyErasureCode(keyInfo);
String ecPolicy;
if (isEc) {
ecPolicy = keyInfo.getReplicationConfig().getReplication();
} else {
ecPolicy = status.isFile() ? "Replicated" : "";
}
return new FileStatusAdapter(
keyInfo.getDataSize(),
keyInfo.getReplicatedSize(),
Expand All @@ -578,7 +593,8 @@ private FileStatusAdapter toFileStatusAdapter(OzoneFileStatusLight status,
null,
getBlockLocations(null),
keyInfo.isEncrypted(),
OzoneClientUtils.isKeyErasureCode(keyInfo)
isEc,
ecPolicy
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
import java.util.stream.Collectors;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.BlockLocation;
import org.apache.hadoop.fs.ContentSummary;
import org.apache.hadoop.fs.CreateFlag;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
Expand Down Expand Up @@ -865,6 +866,91 @@ public BlockLocation[] getFileBlockLocations(FileStatus fileStatus,
}
}

@Override
public ContentSummary getContentSummary(Path f) throws IOException {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low — maintenance risk: getContentSummary() here largely duplicates BasicRootedOzoneFileSystem.getContentSummaryInSpan(). Not a runtime bug today, but future EC-policy changes may need to be applied in two places. Consider extracting a shared helper if you touch this again.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed that the two implementations have a similar shape and may carry some maintenance risk.

However, they use different filesystem-specific paths, adapters, listing, and tracing flows, so extracting a shared helper would broaden this focused fix. I’d prefer to keep this PR scoped and revisit the duplication if these implementations need to change again. 🙏

Path qualifiedPath = f.makeQualified(uri, workingDir);
String key = pathToKey(qualifiedPath);
FileStatusAdapter status;
try {
status = adapter.getFileStatus(key, uri, qualifiedPath, getUsername());
} catch (OMException ex) {
if (ex.getResult().equals(OMException.ResultCodes.KEY_NOT_FOUND)) {
throw new FileNotFoundException("File not found. path:" + f);
}
throw ex;
}

if (status.isFile()) {
long length = status.getLength();
long spaceConsumed = status.getDiskConsumed();
ContentSummary.Builder builder = new ContentSummary.Builder().length(length).
fileCount(1).directoryCount(0).spaceConsumed(spaceConsumed);
applyEcPolicy(builder, status.getErasureCodingPolicy());
return builder.build();
}

long[] summary = {0, 0, 0, 1};
for (FileStatusAdapter s : listStatusAdapter(f)) {
long length = s.getLength();
long spaceConsumed = s.getDiskConsumed();
ContentSummary c;
if (s.isDir()) {
c = getContentSummary(s.getPath());
} else {
ContentSummary.Builder childBuilder = new ContentSummary.Builder().length(length).
fileCount(1).directoryCount(0).spaceConsumed(spaceConsumed);
applyEcPolicy(childBuilder, s.getErasureCodingPolicy());
c = childBuilder.build();
}

summary[0] += c.getLength();
summary[1] += c.getSpaceConsumed();
summary[2] += c.getFileCount();
summary[3] += c.getDirectoryCount();
}

ContentSummary.Builder builder = new ContentSummary.Builder().length(summary[0]).
fileCount(summary[2]).directoryCount(summary[3]).
spaceConsumed(summary[1]);
applyEcPolicy(builder, status.getErasureCodingPolicy());
return builder.build();
}

/**
* Apply the erasure coding policy on the {@link ContentSummary.Builder}.
* Default implementation is a no-op so that this class can compile and run
* against Hadoop 2, where {@code ContentSummary.Builder.erasureCodingPolicy}
* does not exist. The Hadoop 3 subclass overrides this to set the policy.
*/
protected void applyEcPolicy(ContentSummary.Builder builder, String ecPolicy) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Low — Hadoop 2 behavior: applyEcPolicy() is intentionally a no-op here so this compiles against Hadoop 2, where ContentSummary.Builder.erasureCodingPolicy() does not exist. On Hadoop 2 builds, getErasureCodingPolicy() may still be null and ls -e can still throw UnsupportedOperationException.

Fine if Hadoop 3-only support for -e is intended — worth a one-line comment in the PR description or release note.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, Hadoop 2.10.2 doesn’t support ls -e, and I’ve noted that in the PR description.

}

private List<FileStatusAdapter> listStatusAdapter(Path f) throws IOException {
int numEntries = listingPageSize;
LinkedList<FileStatusAdapter> statuses = new LinkedList<>();
List<FileStatusAdapter> tmpStatusList;
String startKey = "";
int entriesAdded;
do {
tmpStatusList = adapter.listStatus(pathToKey(f), false, startKey,
numEntries, uri, workingDir, getUsername(), true);
entriesAdded = 0;
if (!tmpStatusList.isEmpty()) {
if (startKey.isEmpty() || !statuses.getLast().getPath().toString()
.equals(tmpStatusList.get(0).getPath().toString())) {
statuses.addAll(tmpStatusList);
entriesAdded += tmpStatusList.size();
} else {
statuses.addAll(tmpStatusList.subList(1, tmpStatusList.size()));
entriesAdded += tmpStatusList.size() - 1;
}
startKey = pathToKey(statuses.getLast().getPath());
}
} while (entriesAdded > 0);

return statuses;
}

@Override
public short getDefaultReplication() {
return adapter.getDefaultReplication();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,13 @@ private FileStatusAdapter toFileStatusAdapter(OzoneFileStatus status,
OmKeyInfo keyInfo = status.getKeyInfo();
short replication = (short) keyInfo.getReplicationConfig()
.getRequiredNodes();
boolean isEc = OzoneClientUtils.isKeyErasureCode(keyInfo);
String ecPolicy;
if (isEc) {
ecPolicy = keyInfo.getReplicationConfig().getReplication();
} else {
ecPolicy = status.isFile() ? "Replicated" : "";
}
return new FileStatusAdapter(
keyInfo.getDataSize(),
keyInfo.getReplicatedSize(),
Expand All @@ -1051,7 +1058,8 @@ private FileStatusAdapter toFileStatusAdapter(OzoneFileStatus status,
null,
getBlockLocations(status),
OzoneClientUtils.isKeyEncrypted(keyInfo),
OzoneClientUtils.isKeyErasureCode(keyInfo)
isEc,
ecPolicy
);
}

Expand All @@ -1060,6 +1068,13 @@ private FileStatusAdapter toFileStatusAdapter(OzoneFileStatusLight status,
BasicOmKeyInfo keyInfo = status.getKeyInfo();
short replication = (short) keyInfo.getReplicationConfig()
.getRequiredNodes();
boolean isEc = OzoneClientUtils.isKeyErasureCode(keyInfo);
String ecPolicy;
if (isEc) {
ecPolicy = keyInfo.getReplicationConfig().getReplication();
} else {
ecPolicy = status.isFile() ? "Replicated" : "";
}
return new FileStatusAdapter(
keyInfo.getDataSize(),
keyInfo.getReplicatedSize(),
Expand All @@ -1076,7 +1091,8 @@ private FileStatusAdapter toFileStatusAdapter(OzoneFileStatusLight status,
null,
getBlockLocations(null),
keyInfo.isEncrypted(),
OzoneClientUtils.isKeyErasureCode(keyInfo)
isEc,
ecPolicy
);
}

Expand Down Expand Up @@ -1169,7 +1185,7 @@ private static FileStatusAdapter getFileStatusAdapterForVolume(
return new FileStatusAdapter(0L, 0L, path, true, (short)0, 0L,
ozoneVolume.getCreationTime().getEpochSecond() * 1000, 0L,
FsPermission.getDirDefault().toShort(),
owner, group, null, new BlockLocation[0], false, false
owner, group, null, new BlockLocation[0], false, false, ""
);
}

Expand All @@ -1192,14 +1208,16 @@ private static FileStatusAdapter getFileStatusAdapterForBucket(OzoneBucket ozone
UserGroupInformation ugi = UserGroupInformation.createRemoteUser(ozoneBucket.getOwner());
String owner = ugi.getShortUserName();
String group = getGroupName(ugi);
ReplicationConfig rc = ozoneBucket.getReplicationConfig();
boolean isEc = rc != null && rc.getReplicationType() == HddsProtos.ReplicationType.EC;
String ecPolicy = isEc ? rc.getReplication() : "";
return new FileStatusAdapter(0L, 0L, path, true, (short)0, 0L,
ozoneBucket.getCreationTime().getEpochSecond() * 1000, 0L,
FsPermission.getDirDefault().toShort(),
owner, group, null, new BlockLocation[0],
!StringUtils.isEmpty(ozoneBucket.getEncryptionKeyName()),
ozoneBucket.getReplicationConfig() != null &&
ozoneBucket.getReplicationConfig().getReplicationType() ==
HddsProtos.ReplicationType.EC);
isEc,
ecPolicy);
}

/**
Expand All @@ -1225,6 +1243,9 @@ private static FileStatusAdapter getFileStatusAdapterForBucketSnapshot(
ozoneSnapshot.getName(), pathStr);
}
Path path = new Path(pathStr);
ReplicationConfig rc = ozoneBucket.getReplicationConfig();
boolean isEc = rc != null && rc.getReplicationType() == HddsProtos.ReplicationType.EC;
String ecPolicy = isEc ? rc.getReplication() : "";
return new FileStatusAdapter(
ozoneSnapshot.getReferencedSize(),
ozoneSnapshot.getReferencedReplicatedSize(),
Expand All @@ -1233,9 +1254,8 @@ private static FileStatusAdapter getFileStatusAdapterForBucketSnapshot(
FsPermission.getDirDefault().toShort(),
owner, group, null, new BlockLocation[0],
!StringUtils.isEmpty(ozoneBucket.getEncryptionKeyName()),
ozoneBucket.getReplicationConfig() != null &&
ozoneBucket.getReplicationConfig().getReplicationType() ==
HddsProtos.ReplicationType.EC);
isEc,
ecPolicy);
}

/**
Expand Down Expand Up @@ -1264,14 +1284,15 @@ private static FileStatusAdapter getFileStatusAdapterWithSnapshotIndicator(
ozoneBucket.getName(), pathStr);
}
Path path = new Path(pathStr);
boolean isEc = false;
String ecPolicy = "";
return new FileStatusAdapter(0L, 0L, path, true, (short)0, 0L,
ozoneBucket.getCreationTime().getEpochSecond() * 1000, 0L,
FsPermission.getDirDefault().toShort(),
owner, group, null, new BlockLocation[0],
!StringUtils.isEmpty(ozoneBucket.getEncryptionKeyName()),
ozoneBucket.getReplicationConfig() != null &&
ozoneBucket.getReplicationConfig().getReplicationType() ==
HddsProtos.ReplicationType.EC);
isEc,
ecPolicy);
}

/**
Expand All @@ -1286,7 +1307,7 @@ private static FileStatusAdapter getFileStatusAdapterForRoot(URI uri) {
return new FileStatusAdapter(0L, 0L, path, true, (short)0, 0L,
System.currentTimeMillis(), 0L,
FsPermission.getDirDefault().toShort(),
null, null, null, new BlockLocation[0], false, false);
null, null, null, new BlockLocation[0], false, false, "");
}

@Override
Expand Down
Loading