71
71
import org .elasticsearch .common .unit .ByteSizeValue ;
72
72
import org .elasticsearch .common .util .CollectionUtils ;
73
73
import org .elasticsearch .common .util .Maps ;
74
+ import org .elasticsearch .common .util .concurrent .AbstractRunnable ;
74
75
import org .elasticsearch .common .util .concurrent .EsExecutors ;
76
+ import org .elasticsearch .common .util .concurrent .EsRejectedExecutionException ;
75
77
import org .elasticsearch .common .util .concurrent .ListenableFuture ;
76
78
import org .elasticsearch .core .FixForMultiProject ;
77
79
import org .elasticsearch .core .Nullable ;
@@ -1398,9 +1400,34 @@ private void leaveRepoLoop(String repository) {
1398
1400
}
1399
1401
1400
1402
private void finalizeSnapshotEntry (Snapshot snapshot , Metadata metadata , RepositoryData repositoryData ) {
1401
- assert currentlyFinalizing .contains (snapshot .getRepository ());
1402
- assert repositoryOperations .assertNotQueued (snapshot );
1403
- try {
1403
+ threadPool .executor (ThreadPool .Names .SNAPSHOT ).execute (new SnapshotFinalization (snapshot , metadata , repositoryData ));
1404
+ }
1405
+
1406
+ /**
1407
+ * Implements the finalization process for a snapshot: does some preparatory calculations, builds a {@link SnapshotInfo} and a
1408
+ * {@link FinalizeSnapshotContext}, calls {@link Repository#finalizeSnapshot} and handles the outcome by notifying waiting listeners
1409
+ * and triggering the next snapshot-related activity (another finalization, a batch of deletes, etc.)
1410
+ */
1411
+ // This only really makes sense to run against a BlobStoreRepository, and the division of work between this class and
1412
+ // BlobStoreRepository#finalizeSnapshot is kind of awkward and artificial; TODO consolidate all this stuff into one place and simplify
1413
+ private class SnapshotFinalization extends AbstractRunnable {
1414
+
1415
+ private final Snapshot snapshot ;
1416
+ private final Metadata metadata ;
1417
+ private final RepositoryData repositoryData ;
1418
+
1419
+ SnapshotFinalization (Snapshot snapshot , Metadata metadata , RepositoryData repositoryData ) {
1420
+ this .snapshot = snapshot ;
1421
+ this .metadata = metadata ;
1422
+ this .repositoryData = repositoryData ;
1423
+ }
1424
+
1425
+ @ Override
1426
+ protected void doRun () {
1427
+ assert ThreadPool .assertCurrentThreadPool (ThreadPool .Names .SNAPSHOT );
1428
+ assert currentlyFinalizing .contains (snapshot .getRepository ());
1429
+ assert repositoryOperations .assertNotQueued (snapshot );
1430
+
1404
1431
SnapshotsInProgress .Entry entry = SnapshotsInProgress .get (clusterService .state ()).snapshot (snapshot );
1405
1432
final String failure = entry .failure ();
1406
1433
logger .trace ("[{}] finalizing snapshot in repository, state: [{}], failure[{}]" , snapshot , entry .state (), failure );
@@ -1428,7 +1455,9 @@ private void finalizeSnapshotEntry(Snapshot snapshot, Metadata metadata, Reposit
1428
1455
final ListenableFuture <Metadata > metadataListener = new ListenableFuture <>();
1429
1456
final Repository repo = repositoriesService .repository (snapshot .getRepository ());
1430
1457
if (entry .isClone ()) {
1431
- threadPool .executor (ThreadPool .Names .SNAPSHOT ).execute (ActionRunnable .supply (metadataListener , () -> {
1458
+ // This listener is kinda unnecessary since we now always complete it synchronously. It's only here to catch exceptions.
1459
+ // TODO simplify this.
1460
+ ActionListener .completeWith (metadataListener , () -> {
1432
1461
final Metadata existing = repo .getSnapshotGlobalMetadata (entry .source ());
1433
1462
final Metadata .Builder metaBuilder = Metadata .builder (existing );
1434
1463
final Set <Index > existingIndices = new HashSet <>();
@@ -1450,11 +1479,12 @@ private void finalizeSnapshotEntry(Snapshot snapshot, Metadata metadata, Reposit
1450
1479
);
1451
1480
metaBuilder .dataStreams (dataStreamsToCopy , dataStreamAliasesToCopy );
1452
1481
return metaBuilder .build ();
1453
- })) ;
1482
+ });
1454
1483
} else {
1455
1484
metadataListener .onResponse (metadata );
1456
1485
}
1457
1486
metadataListener .addListener (ActionListener .wrap (meta -> {
1487
+ assert ThreadPool .assertCurrentThreadPool (ThreadPool .Names .SNAPSHOT );
1458
1488
final Metadata metaForSnapshot = metadataForSnapshot (entry , meta );
1459
1489
1460
1490
final Map <String , SnapshotInfo .IndexSnapshotDetails > indexSnapshotDetails = Maps .newMapWithExpectedSize (
@@ -1554,7 +1584,20 @@ public void onFailure(Exception e) {
1554
1584
shardGenerations
1555
1585
)
1556
1586
));
1557
- } catch (Exception e ) {
1587
+ }
1588
+
1589
+ @ Override
1590
+ public void onRejection (Exception e ) {
1591
+ if (e instanceof EsRejectedExecutionException esre && esre .isExecutorShutdown ()) {
1592
+ logger .debug ("failing finalization of {} due to shutdown" , snapshot );
1593
+ handleFinalizationFailure (e , snapshot , repositoryData , ShardGenerations .EMPTY );
1594
+ } else {
1595
+ onFailure (e );
1596
+ }
1597
+ }
1598
+
1599
+ @ Override
1600
+ public void onFailure (Exception e ) {
1558
1601
logger .error (Strings .format ("unexpected failure finalizing %s" , snapshot ), e );
1559
1602
assert false : new AssertionError ("unexpected failure finalizing " + snapshot , e );
1560
1603
handleFinalizationFailure (e , snapshot , repositoryData , ShardGenerations .EMPTY );
0 commit comments