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