2020import org .opensearch .core .util .FileSystemUtils ;
2121import org .opensearch .index .IndexModule ;
2222import org .opensearch .index .IndexSettings ;
23+ import org .opensearch .index .store .RemoteSegmentStoreDirectory ;
2324import org .opensearch .indices .replication .common .ReplicationType ;
2425import org .opensearch .snapshots .AbstractSnapshotIntegTestCase ;
2526import org .opensearch .test .FeatureFlagSetter ;
3132import java .util .Collections ;
3233import java .util .Locale ;
3334import java .util .Set ;
35+ import java .util .concurrent .TimeUnit ;
3436import java .util .stream .Collectors ;
3537
3638import static org .opensearch .test .hamcrest .OpenSearchAssertions .assertAcked ;
@@ -83,19 +85,44 @@ public void teardown() {
8385 public void testRemoteRefreshRetryOnFailure () throws Exception {
8486
8587 Path location = randomRepoPath ().toAbsolutePath ();
86- logger . info ( "--> Creating repository={} at the path={}" , REPOSITORY_NAME , location );
88+ setup ( location , randomDoubleBetween ( 0.1 , 0.25 , true ), "metadata" );
8789
90+ // Here we are having flush/refresh after each iteration of indexing. However, the refresh will not always succeed
91+ // due to IOExceptions that are thrown while doing uploadBlobs.
92+ indexData (randomIntBetween (5 , 10 ), randomBoolean ());
93+ logger .info ("--> Indexed data" );
94+
95+ // TODO - Once the segments stats api is available, we need to verify that there were failed upload attempts.
96+ IndicesStatsResponse response = client ().admin ().indices ().stats (new IndicesStatsRequest ()).get ();
97+ assertEquals (1 , response .getShards ().length );
98+
99+ String indexUuid = response .getShards ()[0 ].getShardRouting ().index ().getUUID ();
100+ Path segmentDataRepoPath = location .resolve (String .format (Locale .ROOT , "%s/0/segments/data" , indexUuid ));
101+ String segmentDataLocalPath = String .format (Locale .ROOT , "%s/indices/%s/0/index" , response .getShards ()[0 ].getDataPath (), indexUuid );
102+
103+ logger .info ("--> Verify that the segment files are same on local and repository eventually" );
104+ // This can take time as the retry interval is exponential and maxed at 30s
105+ assertBusy (() -> {
106+ Set <String > filesInLocal = getSegmentFiles (location .getRoot ().resolve (segmentDataLocalPath ));
107+ Set <String > filesInRepo = getSegmentFiles (segmentDataRepoPath );
108+ assertTrue (filesInRepo .containsAll (filesInLocal ));
109+ }, 60 , TimeUnit .SECONDS );
110+ }
111+
112+ private void setup (Path repoLocation , double ioFailureRate , String skipExceptionBlobList ) {
113+ logger .info ("--> Creating repository={} at the path={}" , REPOSITORY_NAME , repoLocation );
88114 // The random_control_io_exception_rate setting ensures that 10-25% of all operations to remote store results in
89115 /// IOException. skip_exception_on_verification_file & skip_exception_on_list_blobs settings ensures that the
90116 // repository creation can happen without failure.
91117 createRepository (
92118 REPOSITORY_NAME ,
93119 "mock" ,
94120 Settings .builder ()
95- .put ("location" , location )
96- .put ("random_control_io_exception_rate" , randomIntBetween ( 10 , 25 ) / 100f )
121+ .put ("location" , repoLocation )
122+ .put ("random_control_io_exception_rate" , ioFailureRate )
97123 .put ("skip_exception_on_verification_file" , true )
98124 .put ("skip_exception_on_list_blobs" , true )
125+ .put ("max_failure_number" , Long .MAX_VALUE )
99126 );
100127
101128 internalCluster ().startDataOnlyNodes (1 );
@@ -105,24 +132,6 @@ public void testRemoteRefreshRetryOnFailure() throws Exception {
105132 logger .info ("--> Cluster is yellow with no initializing shards" );
106133 ensureGreen (INDEX_NAME );
107134 logger .info ("--> Cluster is green" );
108-
109- // Here we are having flush/refresh after each iteration of indexing. However, the refresh will not always succeed
110- // due to IOExceptions that are thrown while doing uploadBlobs.
111- indexData (randomIntBetween (5 , 10 ), randomBoolean ());
112- logger .info ("--> Indexed data" );
113-
114- // TODO - Once the segments stats api is available, we need to verify that there were failed upload attempts.
115- IndicesStatsResponse response = client ().admin ().indices ().stats (new IndicesStatsRequest ()).get ();
116- assertEquals (1 , response .getShards ().length );
117-
118- String indexUuid = response .getShards ()[0 ].getShardRouting ().index ().getUUID ();
119- Path segmentDataRepoPath = location .resolve (String .format (Locale .ROOT , "%s/0/segments/data" , indexUuid ));
120- String segmentDataLocalPath = String .format (Locale .ROOT , "%s/indices/%s/0/index" , response .getShards ()[0 ].getDataPath (), indexUuid );
121-
122- logger .info ("--> Verify that the segment files are same on local and repository eventually" );
123- assertBusy (
124- () -> assertEquals (getSegmentFiles (location .getRoot ().resolve (segmentDataLocalPath )), getSegmentFiles (segmentDataRepoPath ))
125- );
126135 }
127136
128137 /**
@@ -134,15 +143,20 @@ public void testRemoteRefreshRetryOnFailure() throws Exception {
134143 private Set <String > getSegmentFiles (Path location ) {
135144 try {
136145 return Arrays .stream (FileSystemUtils .files (location ))
137- .filter (path -> path .getFileName ().startsWith ("_" ))
146+ .filter (path -> path .getFileName ().toString (). startsWith ("_" ))
138147 .map (path -> path .getFileName ().toString ())
148+ .map (this ::getLocalSegmentFilename )
139149 .collect (Collectors .toSet ());
140150 } catch (IOException exception ) {
141151 logger .error ("Exception occurred while getting segment files" , exception );
142152 }
143153 return Collections .emptySet ();
144154 }
145155
156+ private String getLocalSegmentFilename (String remoteFilename ) {
157+ return remoteFilename .split (RemoteSegmentStoreDirectory .SEGMENT_NAME_UUID_SEPARATOR )[0 ];
158+ }
159+
146160 private IndexResponse indexSingleDoc () {
147161 return client ().prepareIndex (INDEX_NAME )
148162 .setId (UUIDs .randomBase64UUID ())
@@ -153,7 +167,7 @@ private IndexResponse indexSingleDoc() {
153167 private void indexData (int numberOfIterations , boolean invokeFlush ) {
154168 logger .info ("--> Indexing data for {} iterations with flush={}" , numberOfIterations , invokeFlush );
155169 for (int i = 0 ; i < numberOfIterations ; i ++) {
156- int numberOfOperations = randomIntBetween (20 , 50 );
170+ int numberOfOperations = randomIntBetween (1 , 5 );
157171 logger .info ("--> Indexing {} operations in iteration #{}" , numberOfOperations , i );
158172 for (int j = 0 ; j < numberOfOperations ; j ++) {
159173 indexSingleDoc ();
0 commit comments