23
23
import org .opensearch .common .lease .Releasable ;
24
24
import org .opensearch .common .settings .ClusterSettings ;
25
25
import org .opensearch .common .settings .Settings ;
26
+ import org .opensearch .common .unit .TimeValue ;
26
27
import org .opensearch .core .action .ActionListener ;
27
28
import org .opensearch .core .index .shard .ShardId ;
28
29
import org .opensearch .index .engine .InternalEngineFactory ;
34
35
import org .opensearch .index .store .RemoteSegmentStoreDirectory .MetadataFilenameUtils ;
35
36
import org .opensearch .index .store .Store ;
36
37
import org .opensearch .index .store .lockmanager .RemoteStoreLockManager ;
38
+ import org .opensearch .indices .DefaultRemoteStoreSettings ;
37
39
import org .opensearch .indices .RemoteStoreSettings ;
38
40
import org .opensearch .indices .replication .checkpoint .SegmentReplicationCheckpointPublisher ;
39
41
import org .opensearch .indices .replication .common .ReplicationType ;
@@ -91,7 +93,12 @@ public void setup(boolean primary, int numberOfDocs) throws IOException {
91
93
remoteStoreStatsTrackerFactory = new RemoteStoreStatsTrackerFactory (clusterService , Settings .EMPTY );
92
94
remoteStoreStatsTrackerFactory .afterIndexShardCreated (indexShard );
93
95
RemoteSegmentTransferTracker tracker = remoteStoreStatsTrackerFactory .getRemoteSegmentTransferTracker (indexShard .shardId ());
94
- remoteStoreRefreshListener = new RemoteStoreRefreshListener (indexShard , SegmentReplicationCheckpointPublisher .EMPTY , tracker );
96
+ remoteStoreRefreshListener = new RemoteStoreRefreshListener (
97
+ indexShard ,
98
+ SegmentReplicationCheckpointPublisher .EMPTY ,
99
+ tracker ,
100
+ DefaultRemoteStoreSettings .INSTANCE
101
+ );
95
102
}
96
103
97
104
private void indexDocs (int startDocId , int numberOfDocs ) throws IOException {
@@ -176,7 +183,12 @@ public void testRemoteDirectoryInitThrowsException() throws IOException {
176
183
when (remoteStore .directory ()).thenReturn (remoteStoreFilterDirectory );
177
184
178
185
// Since the thrown IOException is caught in the constructor, ctor should be invoked successfully.
179
- new RemoteStoreRefreshListener (shard , SegmentReplicationCheckpointPublisher .EMPTY , mock (RemoteSegmentTransferTracker .class ));
186
+ new RemoteStoreRefreshListener (
187
+ shard ,
188
+ SegmentReplicationCheckpointPublisher .EMPTY ,
189
+ mock (RemoteSegmentTransferTracker .class ),
190
+ DefaultRemoteStoreSettings .INSTANCE
191
+ );
180
192
181
193
// Validate that the stream of metadata file of remoteMetadataDirectory has been opened only once and the
182
194
// listFilesByPrefixInLexicographicOrder has been called twice.
@@ -371,6 +383,33 @@ public void testRefreshSuccessOnSecondAttempt() throws Exception {
371
383
assertNoLagAndTotalUploadsFailed (segmentTracker , 1 );
372
384
}
373
385
386
+ public void testSegmentUploadTimeout () throws Exception {
387
+ // This covers the case where segment upload fails due to timeout
388
+ int succeedOnAttempt = 1 ;
389
+ // We spy on IndexShard.isPrimaryStarted() to validate that we have tried running remote time as per the expectation.
390
+ CountDownLatch refreshCountLatch = new CountDownLatch (succeedOnAttempt );
391
+ CountDownLatch successLatch = new CountDownLatch (2 );
392
+ Tuple <RemoteStoreRefreshListener , RemoteStoreStatsTrackerFactory > tuple = mockIndexShardWithRetryAndScheduleRefresh (
393
+ succeedOnAttempt ,
394
+ refreshCountLatch ,
395
+ successLatch ,
396
+ 1 ,
397
+ new CountDownLatch (0 ),
398
+ true ,
399
+ true
400
+ );
401
+ assertBusy (() -> assertEquals (0 , refreshCountLatch .getCount ()));
402
+ assertBusy (() -> assertEquals (1 , successLatch .getCount ()));
403
+ RemoteStoreStatsTrackerFactory trackerFactory = tuple .v2 ();
404
+ RemoteSegmentTransferTracker segmentTracker = trackerFactory .getRemoteSegmentTransferTracker (indexShard .shardId ());
405
+ assertBusy (() -> {
406
+ assertTrue (segmentTracker .getTotalUploadsFailed () > 1 );
407
+ assertTrue (segmentTracker .getTotalUploadsSucceeded () < 2 );
408
+ });
409
+ // shutdown threadpool for avoid leaking threads
410
+ indexShard .getThreadPool ().shutdownNow ();
411
+ }
412
+
374
413
/**
375
414
* Tests retry flow after snapshot and metadata files have been uploaded to remote store in the failed attempt.
376
415
* Snapshot and metadata files created in failed attempt should not break retry.
@@ -470,6 +509,7 @@ public void testRefreshFailedDueToPrimaryTermMisMatch() throws Exception {
470
509
successLatch ,
471
510
checkpointPublishSucceedOnAttempt ,
472
511
reachedCheckpointPublishLatch ,
512
+ false ,
473
513
false
474
514
);
475
515
@@ -521,7 +561,8 @@ private Tuple<RemoteStoreRefreshListener, RemoteStoreStatsTrackerFactory> mockIn
521
561
successLatch ,
522
562
succeedCheckpointPublishOnAttempt ,
523
563
reachedCheckpointPublishLatch ,
524
- true
564
+ true ,
565
+ false
525
566
);
526
567
}
527
568
@@ -531,7 +572,8 @@ private Tuple<RemoteStoreRefreshListener, RemoteStoreStatsTrackerFactory> mockIn
531
572
CountDownLatch successLatch ,
532
573
int succeedCheckpointPublishOnAttempt ,
533
574
CountDownLatch reachedCheckpointPublishLatch ,
534
- boolean mockPrimaryTerm
575
+ boolean mockPrimaryTerm ,
576
+ boolean testUploadTimeout
535
577
) throws IOException {
536
578
// Create index shard that we will be using to mock different methods in IndexShard for the unit test
537
579
indexShard = newStartedShard (
@@ -565,9 +607,22 @@ private Tuple<RemoteStoreRefreshListener, RemoteStoreStatsTrackerFactory> mockIn
565
607
// Mock (RemoteSegmentStoreDirectory) ((FilterDirectory) ((FilterDirectory) indexShard.remoteStore().directory())
566
608
Store remoteStore = mock (Store .class );
567
609
when (shard .remoteStore ()).thenReturn (remoteStore );
568
- RemoteSegmentStoreDirectory remoteSegmentStoreDirectory =
569
- (RemoteSegmentStoreDirectory ) ((FilterDirectory ) ((FilterDirectory ) indexShard .remoteStore ().directory ()).getDelegate ())
570
- .getDelegate ();
610
+ RemoteSegmentStoreDirectory remoteSegmentStoreDirectory ;
611
+ RemoteDirectory remoteDirectory = mock (RemoteDirectory .class );
612
+
613
+ if (testUploadTimeout ) {
614
+ remoteSegmentStoreDirectory = new RemoteSegmentStoreDirectory (
615
+ remoteDirectory ,
616
+ mock (RemoteDirectory .class ),
617
+ mock (RemoteStoreLockManager .class ),
618
+ indexShard .getThreadPool (),
619
+ indexShard .shardId
620
+ );
621
+ } else {
622
+ remoteSegmentStoreDirectory = (RemoteSegmentStoreDirectory ) ((FilterDirectory ) ((FilterDirectory ) indexShard .remoteStore ()
623
+ .directory ()).getDelegate ()).getDelegate ();
624
+ }
625
+
571
626
FilterDirectory remoteStoreFilterDirectory = new TestFilterDirectory (new TestFilterDirectory (remoteSegmentStoreDirectory ));
572
627
when (remoteStore .directory ()).thenReturn (remoteStoreFilterDirectory );
573
628
@@ -639,7 +694,28 @@ private Tuple<RemoteStoreRefreshListener, RemoteStoreStatsTrackerFactory> mockIn
639
694
RemoteStoreSettings remoteStoreSettings = mock (RemoteStoreSettings .class );
640
695
when (remoteStoreSettings .getMinRemoteSegmentMetadataFiles ()).thenReturn (10 );
641
696
when (shard .getRemoteStoreSettings ()).thenReturn (remoteStoreSettings );
642
- RemoteStoreRefreshListener refreshListener = new RemoteStoreRefreshListener (shard , emptyCheckpointPublisher , tracker );
697
+ if (testUploadTimeout ) {
698
+ when (remoteStoreSettings .getClusterRemoteSegmentTransferTimeout ()).thenReturn (TimeValue .timeValueMillis (10 ));
699
+ doAnswer (invocation -> {
700
+ ActionListener <Void > actionListener = invocation .getArgument (5 );
701
+ indexShard .getThreadPool ().executor (ThreadPool .Names .GENERIC ).execute (() -> {
702
+ try {
703
+ Thread .sleep (30000 );
704
+ } catch (InterruptedException e ) {
705
+ logger .warn ("copyFrom thread interrupted during sleep" );
706
+ }
707
+ actionListener .onResponse (null );
708
+ });
709
+ return true ;
710
+ }).when (remoteDirectory ).copyFrom (any (), any (), any (), any (), any (), any (ActionListener .class ), any (Boolean .class ));
711
+ }
712
+
713
+ RemoteStoreRefreshListener refreshListener = new RemoteStoreRefreshListener (
714
+ shard ,
715
+ emptyCheckpointPublisher ,
716
+ tracker ,
717
+ remoteStoreSettings
718
+ );
643
719
refreshListener .afterRefresh (true );
644
720
return Tuple .tuple (refreshListener , remoteStoreStatsTrackerFactory );
645
721
}
0 commit comments