2323import java .io .IOException ;
2424import java .nio .charset .StandardCharsets ;
2525import java .util .Collection ;
26+ import java .util .HashMap ;
27+ import java .util .Map ;
2628import java .util .concurrent .CompletableFuture ;
2729import javax .ws .rs .core .Response ;
2830import org .apache .hadoop .hdds .conf .OzoneConfiguration ;
2931import org .apache .hadoop .hdds .scm .server .OzoneStorageContainerManager ;
32+ import org .apache .hadoop .hdds .utils .IOUtils ;
3033import org .apache .hadoop .ozone .MiniOzoneCluster ;
3134import org .apache .hadoop .ozone .client .BucketArgs ;
3235import org .apache .hadoop .ozone .client .ObjectStore ;
3639import org .apache .hadoop .ozone .om .OMConfigKeys ;
3740import org .apache .hadoop .ozone .om .helpers .BucketLayout ;
3841import org .apache .hadoop .ozone .om .helpers .OmBucketInfo ;
42+ import org .apache .hadoop .ozone .om .helpers .OmKeyArgs ;
43+ import org .apache .hadoop .ozone .om .helpers .OmKeyLocationInfo ;
3944import org .apache .hadoop .ozone .recon .api .ContainerEndpoint ;
4045import org .apache .hadoop .ozone .recon .api .types .KeyMetadata ;
4146import org .apache .hadoop .ozone .recon .api .types .KeysResponse ;
4247import org .apache .hadoop .ozone .recon .recovery .ReconOMMetadataManager ;
48+ import org .apache .hadoop .ozone .recon .spi .ReconContainerMetadataManager ;
4349import org .apache .hadoop .ozone .recon .spi .impl .OzoneManagerServiceProviderImpl ;
50+ import org .apache .hadoop .ozone .recon .tasks .ContainerKeyMapperHelper ;
4451import org .apache .hadoop .ozone .recon .tasks .ReconTaskControllerImpl ;
4552import org .apache .ozone .test .GenericTestUtils ;
4653import org .junit .jupiter .api .AfterEach ;
@@ -60,6 +67,9 @@ public class TestReconContainerEndpoint {
6067
6168 @ BeforeEach
6269 public void init () throws Exception {
70+ // ContainerKeyMapper tasks share static maps/flags across the JVM; reset so a
71+ // prior test method cannot break mapper state for this cluster instance.
72+ ContainerKeyMapperHelper .clearSharedContainerCountMap ();
6373 OzoneConfiguration conf = new OzoneConfiguration ();
6474 conf .set (OMConfigKeys .OZONE_DEFAULT_BUCKET_LAYOUT ,
6575 OMConfigKeys .OZONE_BUCKET_LAYOUT_FILE_SYSTEM_OPTIMIZED );
@@ -76,13 +86,9 @@ public void init() throws Exception {
7686 }
7787
7888 @ AfterEach
79- public void shutdown () throws IOException {
80- if (client != null ) {
81- client .close ();
82- }
83- if (cluster != null ) {
84- cluster .shutdown ();
85- }
89+ public void shutdown () {
90+ IOUtils .closeQuietly (client , cluster );
91+ ContainerKeyMapperHelper .clearSharedContainerCountMap ();
8692 }
8793
8894 @ Test
@@ -117,15 +123,17 @@ public void testContainerEndpointForFSOLayout() throws Exception {
117123 CompletableFuture <Void > completableFuture =
118124 omMetaManagerUtils .waitForEventBufferEmpty (reconTaskController .getEventBuffer ());
119125 GenericTestUtils .waitFor (completableFuture ::isDone , 100 , 30000 );
126+ completableFuture .join ();
127+ waitUntilReconIndexesKeysForPaths (volName , bucketName ,
128+ nestedDirKey , singleFileKey );
120129
121130 //Search for the bucket from the bucket table and verify its FSO
122131 OmBucketInfo bucketInfo = cluster .getOzoneManager ().getBucketInfo (volName , bucketName );
123132 assertNotNull (bucketInfo );
124133 assertEquals (BucketLayout .FILE_SYSTEM_OPTIMIZED ,
125134 bucketInfo .getBucketLayout ());
126135
127- // Assuming a known container ID that these keys have been written into
128- long testContainerID = 1L ;
136+ long testContainerID = getContainerIdForKey (volName , bucketName , nestedDirKey );
129137
130138 // Query the ContainerEndpoint for the keys in the specified container
131139 Response response = getContainerEndpointResponse (testContainerID );
@@ -145,7 +153,7 @@ public void testContainerEndpointForFSOLayout() throws Exception {
145153 assertEquals ("file1" , keyMetadata .getKey ());
146154 assertEquals ("testvol/fsobucket/dir1/dir2/dir3/file1" , keyMetadata .getCompletePath ());
147155
148- testContainerID = 2L ;
156+ testContainerID = getContainerIdForKey ( volName , bucketName , singleFileKey ) ;
149157 response = getContainerEndpointResponse (testContainerID );
150158 data = (KeysResponse ) response .getEntity ();
151159 keyMetadataList = data .getKeys ();
@@ -186,14 +194,17 @@ public void testContainerEndpointForOBSBucket() throws Exception {
186194 CompletableFuture <Void > completableFuture =
187195 omMetaManagerUtils .waitForEventBufferEmpty (reconTaskController .getEventBuffer ());
188196 GenericTestUtils .waitFor (completableFuture ::isDone , 100 , 30000 );
197+ completableFuture .join ();
198+ waitUntilReconIndexesKeysForPaths (volumeName , obsBucketName , obsSingleFileKey );
189199
190200 // Search for the bucket from the bucket table and verify its OBS
191201 OmBucketInfo bucketInfo = cluster .getOzoneManager ().getBucketInfo (volumeName , obsBucketName );
192202 assertNotNull (bucketInfo );
193203 assertEquals (BucketLayout .OBJECT_STORE , bucketInfo .getBucketLayout ());
194204
195- // Initialize the ContainerEndpoint
196- long containerId = 1L ;
205+ long containerId = getContainerIdForKey (volumeName , obsBucketName ,
206+ obsSingleFileKey );
207+
197208 Response response = getContainerEndpointResponse (containerId );
198209
199210 assertNotNull (response , "Response should not be null." );
@@ -226,6 +237,40 @@ private Response getContainerEndpointResponse(long containerId) {
226237 return containerEndpoint .getKeysForContainer (containerId , 10 , "" );
227238 }
228239
240+ /**
241+ * Wait until Recon's container-key index reflects all written keys (by container id).
242+ * The OM event queue can be empty while a batch is still being processed.
243+ */
244+ private void waitUntilReconIndexesKeysForPaths (String volumeName ,
245+ String bucketName , String ... keyPaths )
246+ throws Exception {
247+ Map <Long , Integer > requiredCountByContainer = new HashMap <>();
248+ for (String keyPath : keyPaths ) {
249+ long containerId =
250+ getContainerIdForKey (volumeName , bucketName , keyPath );
251+ requiredCountByContainer .merge (containerId , 1 , Integer ::sum );
252+ }
253+ ReconContainerMetadataManager mgr =
254+ recon .getReconServer ().getReconContainerMetadataManager ();
255+ TestReconOmMetaManagerUtils .waitUntilReconKeyCounts (mgr , requiredCountByContainer );
256+ }
257+
258+ private long getContainerIdForKey (String volumeName , String bucketName ,
259+ String keyName ) throws IOException {
260+ OmKeyArgs keyArgs = new OmKeyArgs .Builder ()
261+ .setVolumeName (volumeName )
262+ .setBucketName (bucketName )
263+ .setKeyName (keyName )
264+ .build ();
265+ OmKeyLocationInfo location = cluster .getOzoneManager ()
266+ .lookupKey (keyArgs )
267+ .getKeyLocationVersions ()
268+ .get (0 )
269+ .getBlocksLatestVersionOnly ()
270+ .get (0 );
271+ return location .getContainerID ();
272+ }
273+
229274 private void writeTestData (String volumeName , String bucketName ,
230275 String keyPath , String data ) throws Exception {
231276 try (OzoneOutputStream out = client .getObjectStore ().getVolume (volumeName )
0 commit comments