Skip to content

Commit 0f7104e

Browse files
authored
HDDS-11708. Recon ListKeys API should return a proper http response status code if NSSummary rebuild is in progress. (apache#7437)
1 parent 0e0d5e9 commit 0f7104e

6 files changed

Lines changed: 74 additions & 12 deletions

File tree

hadoop-ozone/recon/src/main/java/org/apache/hadoop/ozone/recon/ReconUtils.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@
8080
import org.apache.hadoop.ozone.OzoneConsts;
8181
import org.apache.hadoop.ozone.om.helpers.BucketLayout;
8282
import org.apache.hadoop.ozone.om.helpers.OmKeyInfo;
83+
import org.apache.hadoop.ozone.recon.api.ServiceNotReadyException;
8384
import org.apache.hadoop.ozone.recon.api.types.NSSummary;
8485
import org.apache.hadoop.ozone.recon.api.types.DUResponse;
8586
import org.apache.hadoop.ozone.recon.recovery.ReconOMMetadataManager;
@@ -356,16 +357,14 @@ public static StringBuilder constructFullPathPrefix(long initialParentId, String
356357
if (nsSummary == null) {
357358
log.warn("NSSummary tree is currently being rebuilt or the directory could be in the progress of " +
358359
"deletion, returning empty string for path construction.");
359-
fullPath.setLength(0);
360-
return fullPath;
360+
throw new ServiceNotReadyException("Service is initializing. Please try again later.");
361361
}
362362
if (nsSummary.getParentId() == -1) {
363363
if (rebuildTriggered.compareAndSet(false, true)) {
364364
triggerRebuild(reconNamespaceSummaryManager, omMetadataManager);
365365
}
366366
log.warn("NSSummary tree is currently being rebuilt, returning empty string for path construction.");
367-
fullPath.setLength(0);
368-
return fullPath;
367+
throw new ServiceNotReadyException("Service is initializing. Please try again later.");
369368
}
370369
// On the last pass, dir-name will be empty and parent will be zero, indicating the loop should end.
371370
if (!nsSummary.getDirName().isEmpty()) {

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

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -976,7 +976,7 @@ public Response listKeys(@QueryParam("replicationType") String replicationType,
976976
ListKeysResponse listKeysResponse = new ListKeysResponse();
977977
if (!ReconUtils.isInitializationComplete(omMetadataManager)) {
978978
listKeysResponse.setStatus(ResponseStatus.INITIALIZING);
979-
return Response.ok(listKeysResponse).build();
979+
return Response.status(Response.Status.SERVICE_UNAVAILABLE).entity(listKeysResponse).build();
980980
}
981981
ParamInfo paramInfo = new ParamInfo(replicationType, creationDate, keySize, startPrefix, prevKey,
982982
limit, false, "");
@@ -997,9 +997,9 @@ public Response listKeys(@QueryParam("replicationType") String replicationType,
997997
}
998998

999999
private Response getListKeysResponse(ParamInfo paramInfo) {
1000+
ListKeysResponse listKeysResponse = new ListKeysResponse();
10001001
try {
10011002
paramInfo.setLimit(Math.max(0, paramInfo.getLimit())); // Ensure limit is non-negative
1002-
ListKeysResponse listKeysResponse = new ListKeysResponse();
10031003
listKeysResponse.setPath(paramInfo.getStartPrefix());
10041004
long replicatedTotal = 0;
10051005
long unreplicatedTotal = 0;
@@ -1009,7 +1009,6 @@ private Response getListKeysResponse(ParamInfo paramInfo) {
10091009
omMetadataManager.getKeyTableLite(BucketLayout.LEGACY);
10101010
retrieveKeysFromTable(keyTable, paramInfo, listKeysResponse.getKeys());
10111011

1012-
10131012
// Search keys from FSO layout.
10141013
searchKeysInFSO(paramInfo, listKeysResponse.getKeys());
10151014

@@ -1029,6 +1028,10 @@ private Response getListKeysResponse(ParamInfo paramInfo) {
10291028

10301029
return Response.ok(listKeysResponse).build();
10311030
} catch (RuntimeException e) {
1031+
if (e instanceof ServiceNotReadyException) {
1032+
listKeysResponse.setStatus(ResponseStatus.INITIALIZING);
1033+
return Response.status(Response.Status.SERVICE_UNAVAILABLE).entity(listKeysResponse).build();
1034+
}
10321035
LOG.error("Error generating listKeys response", e);
10331036
return ReconResponseUtils.createInternalServerErrorResponse(
10341037
"Unexpected runtime error while searching keys in OM DB: " + e.getMessage());
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
* <p>
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
* <p>
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.ozone.recon.api;
20+
21+
/**
22+
* This exception being thrown when Rest API service is still initializing and not yet ready.
23+
*/
24+
public class ServiceNotReadyException extends RuntimeException {
25+
public ServiceNotReadyException(String message) {
26+
super(message);
27+
}
28+
}
29+

hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestContainerEndpoint.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,12 @@
6262
import org.apache.hadoop.ozone.recon.scm.ReconPipelineManager;
6363
import org.apache.hadoop.ozone.recon.scm.ReconStorageContainerManagerFacade;
6464
import org.apache.hadoop.ozone.recon.spi.ReconContainerMetadataManager;
65+
import org.apache.hadoop.ozone.recon.spi.ReconNamespaceSummaryManager;
6566
import org.apache.hadoop.ozone.recon.spi.StorageContainerServiceProvider;
6667
import org.apache.hadoop.ozone.recon.spi.impl.OzoneManagerServiceProviderImpl;
6768
import org.apache.hadoop.ozone.recon.spi.impl.StorageContainerServiceProviderImpl;
6869
import org.apache.hadoop.ozone.recon.tasks.ContainerKeyMapperTask;
70+
import org.apache.hadoop.ozone.recon.tasks.NSSummaryTaskWithFSO;
6971
import org.hadoop.ozone.recon.schema.ContainerSchemaDefinition.UnHealthyContainerStates;
7072
import org.hadoop.ozone.recon.schema.tables.pojos.UnhealthyContainers;
7173
import org.junit.jupiter.api.BeforeEach;
@@ -121,6 +123,7 @@ public class TestContainerEndpoint {
121123
LoggerFactory.getLogger(TestContainerEndpoint.class);
122124

123125
private OzoneStorageContainerManager ozoneStorageContainerManager;
126+
private ReconNamespaceSummaryManager reconNamespaceSummaryManager;
124127
private ReconContainerManager reconContainerManager;
125128
private ContainerStateManager containerStateManager;
126129
private ReconPipelineManager reconPipelineManager;
@@ -198,6 +201,8 @@ private void initializeInjector() throws Exception {
198201
containerEndpoint = reconTestInjector.getInstance(ContainerEndpoint.class);
199202
containerHealthSchemaManager =
200203
reconTestInjector.getInstance(ContainerHealthSchemaManager.class);
204+
this.reconNamespaceSummaryManager =
205+
reconTestInjector.getInstance(ReconNamespaceSummaryManager.class);
201206

202207
pipeline = getRandomPipeline();
203208
pipelineID = pipeline.getId();
@@ -472,6 +477,10 @@ public void testGetKeysForContainer() throws IOException {
472477
// Now to check if the ContainerEndpoint also reads the File table
473478
// Set up test data for FSO keys
474479
setUpFSOData();
480+
NSSummaryTaskWithFSO nSSummaryTaskWithFso =
481+
new NSSummaryTaskWithFSO(reconNamespaceSummaryManager,
482+
reconOMMetadataManager, new OzoneConfiguration());
483+
nSSummaryTaskWithFso.reprocessWithFSO(reconOMMetadataManager);
475484
// Reprocess the container key mapper to ensure the latest mapping is used
476485
reprocessContainerKeyMapper();
477486
response = containerEndpoint.getKeysForContainer(20L, -1, "");
@@ -556,6 +565,10 @@ public void testGetKeysForContainerWithPrevKey() throws IOException {
556565
setUpFSOData();
557566
// Reprocess the container key mapper to ensure the latest mapping is used
558567
reprocessContainerKeyMapper();
568+
NSSummaryTaskWithFSO nSSummaryTaskWithFso =
569+
new NSSummaryTaskWithFSO(reconNamespaceSummaryManager,
570+
reconOMMetadataManager, new OzoneConfiguration());
571+
nSSummaryTaskWithFso.reprocessWithFSO(reconOMMetadataManager);
559572
response = containerEndpoint.getKeysForContainer(20L, -1, "/0/1/2/file7");
560573

561574
// Ensure that the expected number of keys is returned

hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestNSSummaryEndpointWithFSO.java

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
import static org.apache.hadoop.ozone.recon.ReconServerConfigKeys.OZONE_RECON_NSSUMMARY_FLUSH_TO_DB_MAX_THRESHOLD;
9090
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
9191
import static org.junit.jupiter.api.Assertions.assertEquals;
92+
import static org.junit.jupiter.api.Assertions.assertThrows;
9293
import static org.mockito.Mockito.mock;
9394
import static org.mockito.Mockito.when;
9495
import static org.mockito.Mockito.anyLong;
@@ -791,8 +792,9 @@ public void testConstructFullPath() throws IOException {
791792
.setParentObjectID(DIR_TWO_OBJECT_ID)
792793
.build();
793794
// Call constructFullPath and verify the result
794-
fullPath = ReconUtils.constructFullPath(keyInfo,
795-
reconNamespaceSummaryManager, reconOMMetadataManager);
795+
OmKeyInfo finalKeyInfo = keyInfo;
796+
assertThrows(ServiceNotReadyException.class, () -> ReconUtils.constructFullPath(finalKeyInfo,
797+
reconNamespaceSummaryManager, reconOMMetadataManager));
796798
}
797799

798800
@Test
@@ -813,8 +815,8 @@ public void testConstructFullPathWithNegativeParentIdTriggersRebuild() throws IO
813815
.setParentObjectID(dirOneObjectId)
814816
.build();
815817

816-
String result = ReconUtils.constructFullPath(keyInfo, mockSummaryManager, mockMetadataManager);
817-
assertEquals("", result, "Expected an empty string return due to rebuild trigger");
818+
assertThrows(ServiceNotReadyException.class, () ->
819+
ReconUtils.constructFullPath(keyInfo, mockSummaryManager, mockMetadataManager));
818820
}
819821

820822
@Test
@@ -836,7 +838,8 @@ public void testLoggingWhenParentIdIsNegative() throws IOException {
836838
.setParentObjectID(1L)
837839
.build();
838840

839-
ReconUtils.constructFullPath(keyInfo, mockManager, null);
841+
assertThrows(ServiceNotReadyException.class, () ->
842+
ReconUtils.constructFullPath(keyInfo, mockManager, null));
840843

841844
// Assert
842845
ArgumentCaptor<String> logCaptor = ArgumentCaptor.forClass(String.class);

hadoop-ozone/recon/src/test/java/org/apache/hadoop/ozone/recon/api/TestOmDBInsightEndPoint.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.apache.hadoop.ozone.recon.api.types.KeyInsightInfoResponse;
4242
import org.apache.hadoop.ozone.recon.api.types.ListKeysResponse;
4343
import org.apache.hadoop.ozone.recon.api.types.NSSummary;
44+
import org.apache.hadoop.ozone.recon.api.types.ResponseStatus;
4445
import org.apache.hadoop.ozone.recon.persistence.AbstractReconSqlDBTest;
4546
import org.apache.hadoop.ozone.recon.persistence.ContainerHealthSchemaManager;
4647
import org.apache.hadoop.ozone.recon.recovery.ReconOMMetadataManager;
@@ -217,6 +218,7 @@ public class TestOmDBInsightEndPoint extends AbstractReconSqlDBTest {
217218
private static final long KEY_TWENTY_TWO_OBJECT_ID = 37L;
218219
private static final long KEY_TWENTY_THREE_OBJECT_ID = 38L;
219220
private static final long KEY_TWENTY_FOUR_OBJECT_ID = 39L;
221+
private static final long KEY_TWENTY_FIVE_OBJECT_ID = 42L;
220222

221223
private static final long EMPTY_OBS_BUCKET_OBJECT_ID = 40L;
222224
private static final long EMPTY_FSO_BUCKET_OBJECT_ID = 41L;
@@ -242,6 +244,7 @@ public class TestOmDBInsightEndPoint extends AbstractReconSqlDBTest {
242244
private static final long KEY_SEVENTEEN_SIZE = 2 * OzoneConsts.KB + 1; // bin 2
243245
private static final long KEY_EIGHTEEN_SIZE = OzoneConsts.KB + 1; // bin 1
244246
private static final long KEY_NINETEEN_SIZE = 2 * OzoneConsts.KB + 1; // bin 2
247+
private static final long KEY_TWENTY_SIZE = OzoneConsts.KB + 1; // bin 1
245248

246249
private static final String OBS_BUCKET_PATH = "/volume1/obs-bucket";
247250
private static final String FSO_BUCKET_PATH = "/volume1/fso-bucket";
@@ -1940,6 +1943,18 @@ public void testListKeysForEmptyOBSBucket() {
19401943
assertEquals("", listKeysResponse.getLastKey());
19411944
}
19421945

1946+
@Test
1947+
public void testListKeysWhenNSSummaryNotInitialized() throws Exception {
1948+
reconNamespaceSummaryManager.clearNSSummaryTable();
1949+
// bucket level DU
1950+
Response bucketResponse =
1951+
omdbInsightEndpoint.listKeys("RATIS", "", 0, FSO_BUCKET_TWO_PATH,
1952+
"", 1000);
1953+
ListKeysResponse listKeysResponse = (ListKeysResponse) bucketResponse.getEntity();
1954+
assertEquals(ResponseStatus.INITIALIZING, listKeysResponse.getStatus());
1955+
assertEquals(Response.Status.SERVICE_UNAVAILABLE.getStatusCode(), bucketResponse.getStatus());
1956+
}
1957+
19431958
@Test
19441959
public void testListKeysForEmptyFSOBucket() {
19451960
Response bucketResponse = omdbInsightEndpoint.listKeys("RATIS", "", 0, EMPTY_FSO_BUCKET_PATH,

0 commit comments

Comments
 (0)