4141import org .opensearch .action .admin .cluster .snapshots .restore .RestoreSnapshotRequest ;
4242import org .opensearch .action .support .IndicesOptions ;
4343import org .opensearch .cluster .ClusterChangedEvent ;
44+ import org .opensearch .cluster .ClusterInfo ;
45+ import org .opensearch .cluster .ClusterInfoService ;
4446import org .opensearch .cluster .ClusterState ;
4547import org .opensearch .cluster .ClusterStateApplier ;
4648import org .opensearch .cluster .ClusterStateTaskConfig ;
6870import org .opensearch .cluster .routing .RoutingChangesObserver ;
6971import org .opensearch .cluster .routing .RoutingTable ;
7072import org .opensearch .cluster .routing .ShardRouting ;
73+ import org .opensearch .cluster .routing .ShardsIterator ;
7174import org .opensearch .cluster .routing .UnassignedInfo ;
7275import org .opensearch .cluster .routing .allocation .AllocationService ;
7376import org .opensearch .cluster .service .ClusterManagerTaskKeys ;
8689import org .opensearch .index .IndexSettings ;
8790import org .opensearch .index .shard .IndexShard ;
8891import org .opensearch .core .index .shard .ShardId ;
92+ import org .opensearch .index .snapshots .IndexShardSnapshotStatus ;
93+ import org .opensearch .index .store .remote .filecache .FileCacheStats ;
94+ import org .opensearch .indices .IndicesService ;
8995import org .opensearch .indices .ShardLimitValidator ;
9096import org .opensearch .repositories .IndexId ;
9197import org .opensearch .repositories .RepositoriesService ;
118124import static org .opensearch .common .util .FeatureFlags .SEARCHABLE_SNAPSHOT_EXTENDED_COMPATIBILITY ;
119125import static org .opensearch .common .util .set .Sets .newHashSet ;
120126import static org .opensearch .index .store .remote .directory .RemoteSnapshotDirectory .SEARCHABLE_SNAPSHOT_EXTENDED_COMPATIBILITY_MINIMUM_VERSION ;
127+ import static org .opensearch .index .store .remote .filecache .FileCache .DATA_TO_FILE_CACHE_SIZE_RATIO_SETTING ;
121128import static org .opensearch .snapshots .SnapshotUtils .filterIndices ;
122129
123130/**
@@ -176,6 +183,10 @@ public class RestoreService implements ClusterStateApplier {
176183
177184 private final ClusterSettings clusterSettings ;
178185
186+ private final IndicesService indicesService ;
187+
188+ private final ClusterInfoService clusterInfoService ;
189+
179190 private final ClusterManagerTaskThrottler .ThrottlingKey restoreSnapshotTaskKey ;
180191
181192 private static final CleanRestoreStateTaskExecutor cleanRestoreStateTaskExecutor = new CleanRestoreStateTaskExecutor ();
@@ -186,8 +197,9 @@ public RestoreService(
186197 AllocationService allocationService ,
187198 MetadataCreateIndexService createIndexService ,
188199 MetadataIndexUpgradeService metadataIndexUpgradeService ,
189- ClusterSettings clusterSettings ,
190- ShardLimitValidator shardLimitValidator
200+ ShardLimitValidator shardLimitValidator ,
201+ IndicesService indicesService ,
202+ ClusterInfoService clusterInfoService
191203 ) {
192204 this .clusterService = clusterService ;
193205 this .repositoriesService = repositoriesService ;
@@ -199,6 +211,8 @@ public RestoreService(
199211 }
200212 this .clusterSettings = clusterService .getClusterSettings ();
201213 this .shardLimitValidator = shardLimitValidator ;
214+ this .indicesService = indicesService ;
215+ this .clusterInfoService = clusterInfoService ;
202216
203217 // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction.
204218 restoreSnapshotTaskKey = clusterService .registerClusterManagerTask (ClusterManagerTaskKeys .RESTORE_SNAPSHOT_KEY , true );
@@ -401,7 +415,6 @@ public ClusterManagerTaskThrottler.ThrottlingKey getClusterManagerThrottlingKey(
401415
402416 @ Override
403417 public ClusterState execute (ClusterState currentState ) {
404- RestoreInProgress restoreInProgress = currentState .custom (RestoreInProgress .TYPE , RestoreInProgress .EMPTY );
405418 // Check if the snapshot to restore is currently being deleted
406419 SnapshotDeletionsInProgress deletionsInProgress = currentState .custom (
407420 SnapshotDeletionsInProgress .TYPE ,
@@ -423,6 +436,7 @@ public ClusterState execute(ClusterState currentState) {
423436 RoutingTable .Builder rtBuilder = RoutingTable .builder (currentState .routingTable ());
424437 final Map <ShardId , RestoreInProgress .ShardRestoreStatus > shards ;
425438 Set <String > aliases = new HashSet <>();
439+ long totalRestorableRemoteIndexesSize = 0 ;
426440
427441 if (indices .isEmpty () == false ) {
428442 // We have some indices to restore
@@ -433,17 +447,14 @@ public ClusterState execute(ClusterState currentState) {
433447 String index = indexEntry .getValue ();
434448 boolean partial = checkPartial (index );
435449
450+ IndexId snapshotIndexId = repositoryData .resolveIndexId (index );
436451 IndexMetadata snapshotIndexMetadata = updateIndexSettings (
437452 metadata .index (index ),
438453 request .indexSettings (),
439454 request .ignoreIndexSettings ()
440455 );
441456 if (IndexModule .Type .REMOTE_SNAPSHOT .match (request .storageType ().toString ())) {
442- snapshotIndexMetadata = addSnapshotToIndexSettings (
443- snapshotIndexMetadata ,
444- snapshot ,
445- repositoryData .resolveIndexId (index )
446- );
457+ snapshotIndexMetadata = addSnapshotToIndexSettings (snapshotIndexMetadata , snapshot , snapshotIndexId );
447458 }
448459 final boolean isSearchableSnapshot = IndexModule .Type .REMOTE_SNAPSHOT .match (
449460 snapshotIndexMetadata .getSettings ().get (IndexModule .INDEX_STORE_TYPE_SETTING .getKey ())
@@ -469,7 +480,7 @@ public ClusterState execute(ClusterState currentState) {
469480 restoreUUID ,
470481 snapshot ,
471482 snapshotInfo .version (),
472- repositoryData . resolveIndexId ( index ) ,
483+ snapshotIndexId ,
473484 isSearchableSnapshot ,
474485 isRemoteStoreShallowCopy ,
475486 request .getSourceRemoteStoreRepository ()
@@ -588,6 +599,14 @@ public ClusterState execute(ClusterState currentState) {
588599 }
589600
590601 for (int shard = 0 ; shard < snapshotIndexMetadata .getNumberOfShards (); shard ++) {
602+ if (IndexModule .Type .REMOTE_SNAPSHOT .match (request .storageType ().toString ())) {
603+ IndexShardSnapshotStatus .Copy shardStatus = repository .getShardSnapshotStatus (
604+ snapshotInfo .snapshotId (),
605+ snapshotIndexId ,
606+ new ShardId (metadata .index (index ).getIndex (), shard )
607+ ).asCopy ();
608+ totalRestorableRemoteIndexesSize += shardStatus .getTotalSize ();
609+ }
591610 if (!ignoreShards .contains (shard )) {
592611 shardsBuilder .put (
593612 new ShardId (renamedIndex , shard ),
@@ -624,6 +643,9 @@ public ClusterState execute(ClusterState currentState) {
624643 }
625644
626645 checkAliasNameConflicts (indices , aliases );
646+ if (IndexModule .Type .REMOTE_SNAPSHOT .match (request .storageType ().toString ())) {
647+ validateSearchableSnapshotRestorable (totalRestorableRemoteIndexesSize );
648+ }
627649
628650 Map <String , DataStream > updatedDataStreams = new HashMap <>(currentState .metadata ().dataStreams ());
629651 updatedDataStreams .putAll (
@@ -823,6 +845,43 @@ private IndexMetadata updateIndexSettings(
823845 return builder .settings (settingsBuilder ).build ();
824846 }
825847
848+ private void validateSearchableSnapshotRestorable (long totalRestorableRemoteIndexesSize ) {
849+ ClusterInfo clusterInfo = clusterInfoService .getClusterInfo ();
850+ int remoteDataToFileCacheRatio = DATA_TO_FILE_CACHE_SIZE_RATIO_SETTING .get (clusterService .getSettings ());
851+ Map <String , FileCacheStats > nodeFileCacheStats = clusterInfo .getNodeFileCacheStats ();
852+ if (nodeFileCacheStats .isEmpty () || remoteDataToFileCacheRatio <= 0 ) {
853+ return ;
854+ }
855+
856+ long totalNodeFileCacheSize = clusterInfo .getNodeFileCacheStats ()
857+ .values ()
858+ .stream ()
859+ .map (fileCacheStats -> fileCacheStats .getTotal ().getBytes ())
860+ .mapToLong (Long ::longValue )
861+ .sum ();
862+
863+ Predicate <ShardRouting > shardRoutingPredicate = shardRouting -> shardRouting .primary ()
864+ && indicesService .indexService (shardRouting .index ()).getIndexSettings ().isRemoteSnapshot ();
865+
866+ ShardsIterator shardsIterator = clusterService .state ()
867+ .routingTable ()
868+ .allShardsSatisfyingPredicate (shardRoutingPredicate );
869+
870+ long totalRestoredRemoteIndexesSize = shardsIterator .getShardRoutings ()
871+ .stream ()
872+ .map (clusterInfo ::getShardSize )
873+ .mapToLong (Long ::longValue )
874+ .sum ();
875+
876+ if (totalRestoredRemoteIndexesSize + totalRestorableRemoteIndexesSize > remoteDataToFileCacheRatio
877+ * totalNodeFileCacheSize ) {
878+ throw new SnapshotRestoreException (
879+ snapshot ,
880+ "Size of the indexes to be restored exceed the file cache bounds. Increase the file cache capacity on the cluster."
881+ );
882+ }
883+ }
884+
826885 @ Override
827886 public void onFailure (String source , Exception e ) {
828887 logger .warn (() -> new ParameterizedMessage ("[{}] failed to restore snapshot" , snapshotId ), e );
0 commit comments