Skip to content

Commit cb5d519

Browse files
HDDS-9534. Support namespace summaries (du, dist & counts) for LEGACY buckets with file system disabled (apache#5517)
1 parent 85c9c97 commit cb5d519

11 files changed

Lines changed: 1303 additions & 226 deletions

File tree

hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/OmUtils.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -743,6 +743,47 @@ public static String normalizeKey(String keyName,
743743
return keyName;
744744
}
745745

746+
/**
747+
* Normalizes a given path up to the bucket level.
748+
*
749+
* This method takes a path as input and normalises uptil the bucket level.
750+
* It handles empty, removes leading slashes, and splits the path into
751+
* segments. It then extracts the volume and bucket names, forming a
752+
* normalized path with a single slash. Finally, any remaining segments are
753+
* joined as the key name, returning the complete standardized path.
754+
*
755+
* @param path The path string to be normalized.
756+
* @return The normalized path string.
757+
*/
758+
public static String normalizePathUptoBucket(String path) {
759+
if (path == null || path.isEmpty()) {
760+
return OM_KEY_PREFIX; // Handle empty path
761+
}
762+
763+
// Remove leading slashes
764+
path = path.replaceAll("^/*", "");
765+
766+
String[] segments = path.split(OM_KEY_PREFIX, -1);
767+
768+
String volumeName = segments[0];
769+
String bucketName = segments.length > 1 ? segments[1] : "";
770+
771+
// Combine volume and bucket.
772+
StringBuilder normalizedPath = new StringBuilder(volumeName);
773+
if (!bucketName.isEmpty()) {
774+
normalizedPath.append(OM_KEY_PREFIX).append(bucketName);
775+
}
776+
777+
// Add remaining segments as the key
778+
if (segments.length > 2) {
779+
normalizedPath.append(OM_KEY_PREFIX).append(
780+
String.join(OM_KEY_PREFIX,
781+
Arrays.copyOfRange(segments, 2, segments.length)));
782+
}
783+
784+
return normalizedPath.toString();
785+
}
786+
746787

747788
/**
748789
* For a given service ID, return list of configured OM hosts.

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/handlers/BucketHandler.java

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
*/
1818
package org.apache.hadoop.ozone.recon.api.handlers;
1919

20+
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
2021
import org.apache.hadoop.hdds.scm.container.ContainerManager;
2122
import org.apache.hadoop.hdds.scm.server.OzoneStorageContainerManager;
2223
import org.apache.hadoop.hdds.utils.db.Table;
24+
import org.apache.hadoop.ozone.om.OMConfigKeys;
2325
import org.apache.hadoop.ozone.om.helpers.OmDirectoryInfo;
2426
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
2527
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
@@ -163,6 +165,8 @@ public static BucketHandler getBucketHandler(
163165
ReconOMMetadataManager omMetadataManager,
164166
OzoneStorageContainerManager reconSCM,
165167
OmBucketInfo bucketInfo) throws IOException {
168+
// Check if enableFileSystemPaths flag is set to true.
169+
boolean enableFileSystemPaths = isEnableFileSystemPaths(omMetadataManager);
166170

167171
// If bucketInfo is null then entity type is UNKNOWN
168172
if (Objects.isNull(bucketInfo)) {
@@ -172,10 +176,17 @@ public static BucketHandler getBucketHandler(
172176
.equals(BucketLayout.FILE_SYSTEM_OPTIMIZED)) {
173177
return new FSOBucketHandler(reconNamespaceSummaryManager,
174178
omMetadataManager, reconSCM, bucketInfo);
175-
} else if (bucketInfo.getBucketLayout()
176-
.equals(BucketLayout.LEGACY)) {
177-
return new LegacyBucketHandler(reconNamespaceSummaryManager,
178-
omMetadataManager, reconSCM, bucketInfo);
179+
} else if (bucketInfo.getBucketLayout().equals(BucketLayout.LEGACY)) {
180+
// Choose handler based on enableFileSystemPaths flag for legacy layout.
181+
// If enableFileSystemPaths is false, then the legacy bucket is treated
182+
// as an OBS bucket.
183+
if (enableFileSystemPaths) {
184+
return new LegacyBucketHandler(reconNamespaceSummaryManager,
185+
omMetadataManager, reconSCM, bucketInfo);
186+
} else {
187+
return new OBSBucketHandler(reconNamespaceSummaryManager,
188+
omMetadataManager, reconSCM, bucketInfo);
189+
}
179190
} else if (bucketInfo.getBucketLayout()
180191
.equals(BucketLayout.OBJECT_STORE)) {
181192
return new OBSBucketHandler(reconNamespaceSummaryManager,
@@ -188,6 +199,22 @@ public static BucketHandler getBucketHandler(
188199
}
189200
}
190201

202+
/**
203+
* Determines whether FileSystemPaths are enabled for Legacy Buckets
204+
* based on the Ozone configuration.
205+
*
206+
* @param ReconOMMetadataManager Instance
207+
* @return True if FileSystemPaths are enabled, false otherwise.
208+
*/
209+
private static boolean isEnableFileSystemPaths(ReconOMMetadataManager omMetadataManager) {
210+
OzoneConfiguration configuration = omMetadataManager.getOzoneConfiguration();
211+
if (configuration == null) {
212+
configuration = new OzoneConfiguration();
213+
}
214+
return configuration.getBoolean(OMConfigKeys.OZONE_OM_ENABLE_FILESYSTEM_PATHS,
215+
OMConfigKeys.OZONE_OM_ENABLE_FILESYSTEM_PATHS_DEFAULT);
216+
}
217+
191218
public static BucketHandler getBucketHandler(
192219
ReconNamespaceSummaryManager reconNamespaceSummaryManager,
193220
ReconOMMetadataManager omMetadataManager,

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/api/handlers/EntityHandler.java

Lines changed: 86 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import org.apache.hadoop.hdds.scm.server.OzoneStorageContainerManager;
2121
import org.apache.hadoop.ozone.OmUtils;
22+
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
2223
import org.apache.hadoop.ozone.recon.ReconConstants;
2324
import org.apache.hadoop.ozone.recon.api.types.NamespaceSummaryResponse;
2425
import org.apache.hadoop.ozone.recon.api.types.DUResponse;
@@ -60,9 +61,18 @@ public EntityHandler(
6061
this.omMetadataManager = omMetadataManager;
6162
this.reconSCM = reconSCM;
6263
this.bucketHandler = bucketHandler;
63-
normalizedPath = normalizePath(path);
64-
names = parseRequestPath(normalizedPath);
6564

65+
// Defaulting to FILE_SYSTEM_OPTIMIZED if bucketHandler is null
66+
BucketLayout layout =
67+
(bucketHandler != null) ? bucketHandler.getBucketLayout() :
68+
BucketLayout.FILE_SYSTEM_OPTIMIZED;
69+
70+
// Normalize the path based on the determined layout
71+
normalizedPath = normalizePath(path, layout);
72+
73+
// Choose the parsing method based on the bucket layout
74+
names = (layout == BucketLayout.OBJECT_STORE) ?
75+
parseObjectStorePath(normalizedPath) : parseRequestPath(normalizedPath);
6676
}
6777

6878
public abstract NamespaceSummaryResponse getSummaryResponse()
@@ -118,7 +128,8 @@ public static EntityHandler getEntityHandler(
118128
String path) throws IOException {
119129
BucketHandler bucketHandler;
120130

121-
String normalizedPath = normalizePath(path);
131+
String normalizedPath =
132+
normalizePath(path, BucketLayout.FILE_SYSTEM_OPTIMIZED);
122133
String[] names = parseRequestPath(normalizedPath);
123134
if (path.equals(OM_KEY_PREFIX)) {
124135
return EntityType.ROOT.create(reconNamespaceSummaryManager,
@@ -156,23 +167,36 @@ public static EntityHandler getEntityHandler(
156167
String volName = names[0];
157168
String bucketName = names[1];
158169

159-
String keyName = BucketHandler.getKeyName(names);
160-
170+
// Assuming getBucketHandler already validates volume and bucket existence
161171
bucketHandler = BucketHandler.getBucketHandler(
162-
reconNamespaceSummaryManager,
163-
omMetadataManager, reconSCM,
164-
volName, bucketName);
172+
reconNamespaceSummaryManager, omMetadataManager, reconSCM, volName,
173+
bucketName);
165174

166-
// check if either volume or bucket doesn't exist
167-
if (bucketHandler == null
168-
|| !omMetadataManager.volumeExists(volName)
169-
|| !bucketHandler.bucketExists(volName, bucketName)) {
175+
if (bucketHandler == null) {
170176
return EntityType.UNKNOWN.create(reconNamespaceSummaryManager,
171-
omMetadataManager, reconSCM, null, path);
177+
omMetadataManager, reconSCM, null, path);
178+
}
179+
180+
// Directly handle path normalization and parsing based on the layout
181+
if (bucketHandler.getBucketLayout() == BucketLayout.OBJECT_STORE) {
182+
String[] parsedObjectLayoutPath = parseObjectStorePath(
183+
normalizePath(path, bucketHandler.getBucketLayout()));
184+
if (parsedObjectLayoutPath == null) {
185+
return EntityType.UNKNOWN.create(reconNamespaceSummaryManager,
186+
omMetadataManager, reconSCM, null, path);
187+
}
188+
// Use the key part directly from the parsed path
189+
return bucketHandler.determineKeyPath(parsedObjectLayoutPath[2])
190+
.create(reconNamespaceSummaryManager, omMetadataManager, reconSCM,
191+
bucketHandler, path);
192+
} else {
193+
// Use the existing names array for non-OBJECT_STORE layouts to derive
194+
// the keyName
195+
String keyName = BucketHandler.getKeyName(names);
196+
return bucketHandler.determineKeyPath(keyName)
197+
.create(reconNamespaceSummaryManager, omMetadataManager, reconSCM,
198+
bucketHandler, path);
172199
}
173-
return bucketHandler.determineKeyPath(keyName)
174-
.create(reconNamespaceSummaryManager,
175-
omMetadataManager, reconSCM, bucketHandler, path);
176200
}
177201
}
178202

@@ -256,7 +280,52 @@ public static String[] parseRequestPath(String path) {
256280
return names;
257281
}
258282

259-
private static String normalizePath(String path) {
283+
/**
284+
* Splits an object store path into volume, bucket, and key name components.
285+
*
286+
* This method parses a path of the format "/volumeName/bucketName/keyName",
287+
* including paths with additional '/' characters within the key name. It's
288+
* designed for object store paths where the first three '/' characters
289+
* separate the root, volume and bucket names from the key name.
290+
*
291+
* @param path The object store path to parse, starting with a slash.
292+
* @return A String array with three elements: volume name, bucket name, and
293+
* key name, or {null} if the path format is invalid.
294+
*/
295+
public static String[] parseObjectStorePath(String path) {
296+
// Removing the leading slash for correct splitting
297+
path = path.substring(1);
298+
299+
// Splitting the modified path by "/", limiting to 3 parts
300+
String[] parts = path.split("/", 3);
301+
302+
// Checking if we correctly obtained 3 parts after removing the leading slash
303+
if (parts.length <= 3) {
304+
return parts;
305+
} else {
306+
return null;
307+
}
308+
}
309+
310+
/**
311+
* Normalizes a given path based on the specified bucket layout.
312+
*
313+
* This method adjusts the path according to the bucket layout.
314+
* For {OBJECT_STORE Layout}, it normalizes the path up to the bucket level
315+
* using OmUtils.normalizePathUptoBucket. For other layouts, it
316+
* normalizes the entire path, including the key, using
317+
* OmUtils.normalizeKey, and does not preserve any trailing slashes.
318+
* The normalized path will always be prefixed with OM_KEY_PREFIX to ensure it
319+
* is consistent with the expected format for object storage paths in Ozone.
320+
*
321+
* @param path
322+
* @param bucketLayout
323+
* @return A normalized path
324+
*/
325+
private static String normalizePath(String path, BucketLayout bucketLayout) {
326+
if (bucketLayout == BucketLayout.OBJECT_STORE) {
327+
return OM_KEY_PREFIX + OmUtils.normalizePathUptoBucket(path);
328+
}
260329
return OM_KEY_PREFIX + OmUtils.normalizeKey(path, false);
261330
}
262331
}

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/recovery/ReconOMMetadataManager.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.IOException;
2323
import java.util.List;
2424

25+
import org.apache.hadoop.hdds.conf.OzoneConfiguration;
2526
import org.apache.hadoop.ozone.om.OMMetadataManager;
2627
import org.apache.hadoop.ozone.om.helpers.OmBucketInfo;
2728
import org.apache.hadoop.ozone.om.helpers.OmVolumeArgs;
@@ -105,4 +106,11 @@ List<OmBucketInfo> listBucketsUnderVolume(String volumeName,
105106
*/
106107
List<OmBucketInfo> listBucketsUnderVolume(
107108
String volumeName) throws IOException;
109+
110+
/**
111+
* Return the OzoneConfiguration instance used by Recon.
112+
* @return
113+
*/
114+
OzoneConfiguration getOzoneConfiguration();
115+
108116
}

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/recovery/ReconOmMetadataManagerImpl.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,11 @@ public List<OmBucketInfo> listBucketsUnderVolume(final String volumeName)
291291
Integer.MAX_VALUE);
292292
}
293293

294+
@Override
295+
public OzoneConfiguration getOzoneConfiguration() {
296+
return ozoneConfiguration;
297+
}
298+
294299
private List<OmBucketInfo> listAllBuckets(final int maxNumberOfBuckets)
295300
throws IOException {
296301
List<OmBucketInfo> result = new ArrayList<>();

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/tasks/NSSummaryTask.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
*/
6464
public class NSSummaryTask implements ReconOmTask {
6565
private static final Logger LOG =
66-
LoggerFactory.getLogger(NSSummaryTask.class);
66+
LoggerFactory.getLogger(NSSummaryTask.class);
6767

6868
private final ReconNamespaceSummaryManager reconNamespaceSummaryManager;
6969
private final ReconOMMetadataManager reconOMMetadataManager;
@@ -173,4 +173,3 @@ public Pair<String, Boolean> reprocess(OMMetadataManager omMetadataManager) {
173173
}
174174

175175
}
176-

0 commit comments

Comments
 (0)