4242import org .opensearch .action .admin .cluster .snapshots .restore .RestoreSnapshotRequest ;
4343import org .opensearch .action .support .IndicesOptions ;
4444import org .opensearch .cluster .ClusterChangedEvent ;
45+ import org .opensearch .cluster .ClusterInfo ;
4546import org .opensearch .cluster .ClusterState ;
4647import org .opensearch .cluster .ClusterStateApplier ;
4748import org .opensearch .cluster .ClusterStateTaskConfig ;
7071import org .opensearch .cluster .routing .RoutingChangesObserver ;
7172import org .opensearch .cluster .routing .RoutingTable ;
7273import org .opensearch .cluster .routing .ShardRouting ;
74+ import org .opensearch .cluster .routing .ShardsIterator ;
7375import org .opensearch .cluster .routing .UnassignedInfo ;
7476import org .opensearch .cluster .routing .allocation .AllocationService ;
7577import org .opensearch .cluster .service .ClusterManagerTaskKeys ;
8890import org .opensearch .index .IndexSettings ;
8991import org .opensearch .index .shard .IndexShard ;
9092import org .opensearch .core .index .shard .ShardId ;
93+ import org .opensearch .index .snapshots .IndexShardSnapshotStatus ;
94+ import org .opensearch .index .store .remote .filecache .FileCacheStats ;
95+ import org .opensearch .indices .IndicesService ;
9196import org .opensearch .indices .ShardLimitValidator ;
9297import org .opensearch .repositories .IndexId ;
9398import org .opensearch .repositories .RepositoriesService ;
105110import java .util .Set ;
106111import java .util .function .Function ;
107112import java .util .function .Predicate ;
113+ import java .util .function .Supplier ;
108114import java .util .stream .Collectors ;
109115
110116import static java .util .Collections .unmodifiableSet ;
120126import static org .opensearch .common .util .FeatureFlags .SEARCHABLE_SNAPSHOT_EXTENDED_COMPATIBILITY ;
121127import static org .opensearch .common .util .set .Sets .newHashSet ;
122128import static org .opensearch .index .store .remote .directory .RemoteSnapshotDirectory .SEARCHABLE_SNAPSHOT_EXTENDED_COMPATIBILITY_MINIMUM_VERSION ;
129+ import static org .opensearch .index .store .remote .filecache .FileCache .DATA_TO_FILE_CACHE_SIZE_RATIO_SETTING ;
130+ import static org .opensearch .node .Node .NODE_SEARCH_CACHE_SIZE_SETTING ;
123131import static org .opensearch .snapshots .SnapshotUtils .filterIndices ;
124132
125133/**
@@ -178,6 +186,10 @@ public class RestoreService implements ClusterStateApplier {
178186
179187 private final ClusterSettings clusterSettings ;
180188
189+ private final IndicesService indicesService ;
190+
191+ private final Supplier <ClusterInfo > clusterInfoSupplier ;
192+
181193 private final ClusterManagerTaskThrottler .ThrottlingKey restoreSnapshotTaskKey ;
182194
183195 private static final CleanRestoreStateTaskExecutor cleanRestoreStateTaskExecutor = new CleanRestoreStateTaskExecutor ();
@@ -188,8 +200,9 @@ public RestoreService(
188200 AllocationService allocationService ,
189201 MetadataCreateIndexService createIndexService ,
190202 MetadataIndexUpgradeService metadataIndexUpgradeService ,
191- ClusterSettings clusterSettings ,
192- ShardLimitValidator shardLimitValidator
203+ ShardLimitValidator shardLimitValidator ,
204+ IndicesService indicesService ,
205+ Supplier <ClusterInfo > clusterInfoSupplier
193206 ) {
194207 this .clusterService = clusterService ;
195208 this .repositoriesService = repositoriesService ;
@@ -201,6 +214,8 @@ public RestoreService(
201214 }
202215 this .clusterSettings = clusterService .getClusterSettings ();
203216 this .shardLimitValidator = shardLimitValidator ;
217+ this .indicesService = indicesService ;
218+ this .clusterInfoSupplier = clusterInfoSupplier ;
204219
205220 // Task is onboarded for throttling, it will get retried from associated TransportClusterManagerNodeAction.
206221 restoreSnapshotTaskKey = clusterService .registerClusterManagerTask (ClusterManagerTaskKeys .RESTORE_SNAPSHOT_KEY , true );
@@ -447,7 +462,9 @@ public ClusterState execute(ClusterState currentState) {
447462 ClusterBlocks .Builder blocks = ClusterBlocks .builder ().blocks (currentState .blocks ());
448463 RoutingTable .Builder rtBuilder = RoutingTable .builder (currentState .routingTable ());
449464 final Map <ShardId , RestoreInProgress .ShardRestoreStatus > shards ;
465+ final boolean isRemoteSnapshot = IndexModule .Type .REMOTE_SNAPSHOT .match (request .storageType ().toString ());
450466 Set <String > aliases = new HashSet <>();
467+ long totalRestorableRemoteIndexesSize = 0 ;
451468
452469 if (indices .isEmpty () == false ) {
453470 // We have some indices to restore
@@ -458,17 +475,14 @@ public ClusterState execute(ClusterState currentState) {
458475 String index = indexEntry .getValue ();
459476 boolean partial = checkPartial (index );
460477
478+ IndexId snapshotIndexId = repositoryData .resolveIndexId (index );
461479 IndexMetadata snapshotIndexMetadata = updateIndexSettings (
462480 metadata .index (index ),
463481 request .indexSettings (),
464482 request .ignoreIndexSettings ()
465483 );
466- if (IndexModule .Type .REMOTE_SNAPSHOT .match (request .storageType ().toString ())) {
467- snapshotIndexMetadata = addSnapshotToIndexSettings (
468- snapshotIndexMetadata ,
469- snapshot ,
470- repositoryData .resolveIndexId (index )
471- );
484+ if (isRemoteSnapshot ) {
485+ snapshotIndexMetadata = addSnapshotToIndexSettings (snapshotIndexMetadata , snapshot , snapshotIndexId );
472486 }
473487 final boolean isSearchableSnapshot = IndexModule .Type .REMOTE_SNAPSHOT .match (
474488 snapshotIndexMetadata .getSettings ().get (IndexModule .INDEX_STORE_TYPE_SETTING .getKey ())
@@ -494,7 +508,7 @@ public ClusterState execute(ClusterState currentState) {
494508 restoreUUID ,
495509 snapshot ,
496510 snapshotInfo .version (),
497- repositoryData . resolveIndexId ( index ) ,
511+ snapshotIndexId ,
498512 isSearchableSnapshot ,
499513 isRemoteStoreShallowCopy ,
500514 request .getSourceRemoteStoreRepository ()
@@ -618,6 +632,14 @@ public ClusterState execute(ClusterState currentState) {
618632 }
619633
620634 for (int shard = 0 ; shard < snapshotIndexMetadata .getNumberOfShards (); shard ++) {
635+ if (isRemoteSnapshot ) {
636+ IndexShardSnapshotStatus .Copy shardStatus = repository .getShardSnapshotStatus (
637+ snapshotInfo .snapshotId (),
638+ snapshotIndexId ,
639+ new ShardId (metadata .index (index ).getIndex (), shard )
640+ ).asCopy ();
641+ totalRestorableRemoteIndexesSize += shardStatus .getTotalSize ();
642+ }
621643 if (!ignoreShards .contains (shard )) {
622644 shardsBuilder .put (
623645 new ShardId (renamedIndex , shard ),
@@ -654,6 +676,9 @@ public ClusterState execute(ClusterState currentState) {
654676 }
655677
656678 checkAliasNameConflicts (indices , aliases );
679+ if (isRemoteSnapshot ) {
680+ validateSearchableSnapshotRestorable (totalRestorableRemoteIndexesSize );
681+ }
657682
658683 Map <String , DataStream > updatedDataStreams = new HashMap <>(currentState .metadata ().dataStreams ());
659684 updatedDataStreams .putAll (
@@ -853,6 +878,45 @@ private IndexMetadata updateIndexSettings(
853878 return builder .settings (settingsBuilder ).build ();
854879 }
855880
881+ private void validateSearchableSnapshotRestorable (long totalRestorableRemoteIndexesSize ) {
882+ ClusterInfo clusterInfo = clusterInfoSupplier .get ();
883+ double remoteDataToFileCacheRatio = DATA_TO_FILE_CACHE_SIZE_RATIO_SETTING .get (clusterService .getSettings ());
884+ Map <String , FileCacheStats > nodeFileCacheStats = clusterInfo .getNodeFileCacheStats ();
885+ if (nodeFileCacheStats .isEmpty () || remoteDataToFileCacheRatio <= 0.01f ) {
886+ return ;
887+ }
888+
889+ long totalNodeFileCacheSize = clusterInfo .getNodeFileCacheStats ()
890+ .values ()
891+ .stream ()
892+ .map (fileCacheStats -> fileCacheStats .getTotal ().getBytes ())
893+ .mapToLong (Long ::longValue )
894+ .sum ();
895+
896+ Predicate <ShardRouting > isRemoteSnapshotShard = shardRouting -> shardRouting .primary ()
897+ && indicesService .indexService (shardRouting .index ()).getIndexSettings ().isRemoteSnapshot ();
898+
899+ ShardsIterator shardsIterator = clusterService .state ()
900+ .routingTable ()
901+ .allShardsSatisfyingPredicate (isRemoteSnapshotShard );
902+
903+ long totalRestoredRemoteIndexesSize = shardsIterator .getShardRoutings ()
904+ .stream ()
905+ .map (clusterInfo ::getShardSize )
906+ .mapToLong (Long ::longValue )
907+ .sum ();
908+
909+ if (totalRestoredRemoteIndexesSize + totalRestorableRemoteIndexesSize > remoteDataToFileCacheRatio
910+ * totalNodeFileCacheSize ) {
911+ throw new SnapshotRestoreException (
912+ snapshot ,
913+ "Size of the indexes to be restored exceeds the file cache bounds. Increase the file cache capacity on the cluster nodes using "
914+ + NODE_SEARCH_CACHE_SIZE_SETTING .getKey ()
915+ + " setting."
916+ );
917+ }
918+ }
919+
856920 @ Override
857921 public void onFailure (String source , Exception e ) {
858922 logger .warn (() -> new ParameterizedMessage ("[{}] failed to restore snapshot" , snapshotId ), e );
0 commit comments