1010import org .elasticsearch .action .ActionListener ;
1111import org .elasticsearch .action .admin .cluster .state .ClusterStateRequest ;
1212import org .elasticsearch .action .admin .cluster .state .ClusterStateResponse ;
13+ import org .elasticsearch .action .admin .indices .stats .IndexShardStats ;
14+ import org .elasticsearch .action .admin .indices .stats .IndexStats ;
15+ import org .elasticsearch .action .admin .indices .stats .IndicesStatsRequest ;
16+ import org .elasticsearch .action .admin .indices .stats .IndicesStatsResponse ;
17+ import org .elasticsearch .action .admin .indices .stats .ShardStats ;
1318import org .elasticsearch .client .Client ;
1419import org .elasticsearch .cluster .ClusterState ;
1520import org .elasticsearch .cluster .metadata .IndexMetaData ;
21+ import org .elasticsearch .common .CheckedConsumer ;
22+ import org .elasticsearch .index .engine .CommitStats ;
23+ import org .elasticsearch .index .engine .Engine ;
24+ import org .elasticsearch .index .shard .ShardId ;
1625import org .elasticsearch .license .RemoteClusterLicenseChecker ;
1726import org .elasticsearch .license .XPackLicenseState ;
1827import org .elasticsearch .rest .RestStatus ;
2130import java .util .Collections ;
2231import java .util .Locale ;
2332import java .util .Objects ;
33+ import java .util .function .BiConsumer ;
2434import java .util .function .BooleanSupplier ;
2535import java .util .function .Consumer ;
2636import java .util .function .Function ;
@@ -58,23 +68,24 @@ public boolean isCcrAllowed() {
5868 }
5969
6070 /**
61- * Fetches the leader index metadata from the remote cluster. Before fetching the index metadata, the remote cluster is checked for
62- * license compatibility with CCR. If the remote cluster is not licensed for CCR, the {@code onFailure} consumer is is invoked.
63- * Otherwise, the specified consumer is invoked with the leader index metadata fetched from the remote cluster.
71+ * Fetches the leader index metadata and history UUIDs for leader index shards from the remote cluster.
72+ * Before fetching the index metadata, the remote cluster is checked for license compatibility with CCR.
73+ * If the remote cluster is not licensed for CCR, the {@code onFailure} consumer is is invoked. Otherwise,
74+ * the specified consumer is invoked with the leader index metadata fetched from the remote cluster.
6475 *
65- * @param client the client
66- * @param clusterAlias the remote cluster alias
67- * @param leaderIndex the name of the leader index
68- * @param onFailure the failure consumer
69- * @param leaderIndexMetadataConsumer the leader index metadata consumer
70- * @param <T> the type of response the listener is waiting for
76+ * @param client the client
77+ * @param clusterAlias the remote cluster alias
78+ * @param leaderIndex the name of the leader index
79+ * @param onFailure the failure consumer
80+ * @param consumer the consumer for supplying the leader index metadata and historyUUIDs of all leader shards
81+ * @param <T> the type of response the listener is waiting for
7182 */
72- public <T > void checkRemoteClusterLicenseAndFetchLeaderIndexMetadata (
83+ public <T > void checkRemoteClusterLicenseAndFetchLeaderIndexMetadataAndHistoryUUIDs (
7384 final Client client ,
7485 final String clusterAlias ,
7586 final String leaderIndex ,
7687 final Consumer <Exception > onFailure ,
77- final Consumer < IndexMetaData > leaderIndexMetadataConsumer ) {
88+ final BiConsumer < String [], IndexMetaData > consumer ) {
7889
7990 final ClusterStateRequest request = new ClusterStateRequest ();
8091 request .clear ();
@@ -85,7 +96,13 @@ public <T> void checkRemoteClusterLicenseAndFetchLeaderIndexMetadata(
8596 clusterAlias ,
8697 request ,
8798 onFailure ,
88- leaderClusterState -> leaderIndexMetadataConsumer .accept (leaderClusterState .getMetaData ().index (leaderIndex )),
99+ leaderClusterState -> {
100+ IndexMetaData leaderIndexMetaData = leaderClusterState .getMetaData ().index (leaderIndex );
101+ final Client leaderClient = client .getRemoteClusterClient (clusterAlias );
102+ fetchLeaderHistoryUUIDs (leaderClient , leaderIndexMetaData , onFailure , historyUUIDs -> {
103+ consumer .accept (historyUUIDs , leaderIndexMetaData );
104+ });
105+ },
89106 licenseCheck -> indexMetadataNonCompliantRemoteLicense (leaderIndex , licenseCheck ),
90107 e -> indexMetadataUnknownRemoteLicense (leaderIndex , clusterAlias , e ));
91108 }
@@ -168,6 +185,58 @@ public void onFailure(final Exception e) {
168185 });
169186 }
170187
188+ /**
189+ * Fetches the history UUIDs for leader index on per shard basis using the specified leaderClient.
190+ *
191+ * @param leaderClient the leader client
192+ * @param leaderIndexMetaData the leader index metadata
193+ * @param onFailure the failure consumer
194+ * @param historyUUIDConsumer the leader index history uuid and consumer
195+ */
196+ // NOTE: Placed this method here; in order to avoid duplication of logic for fetching history UUIDs
197+ // in case of following a local or a remote cluster.
198+ public void fetchLeaderHistoryUUIDs (
199+ final Client leaderClient ,
200+ final IndexMetaData leaderIndexMetaData ,
201+ final Consumer <Exception > onFailure ,
202+ final Consumer <String []> historyUUIDConsumer ) {
203+
204+ String leaderIndex = leaderIndexMetaData .getIndex ().getName ();
205+ CheckedConsumer <IndicesStatsResponse , Exception > indicesStatsHandler = indicesStatsResponse -> {
206+ IndexStats indexStats = indicesStatsResponse .getIndices ().get (leaderIndex );
207+ String [] historyUUIDs = new String [leaderIndexMetaData .getNumberOfShards ()];
208+ for (IndexShardStats indexShardStats : indexStats ) {
209+ for (ShardStats shardStats : indexShardStats ) {
210+ // Ignore replica shards as they may not have yet started and
211+ // we just end up overwriting slots in historyUUIDs
212+ if (shardStats .getShardRouting ().primary () == false ) {
213+ continue ;
214+ }
215+
216+ CommitStats commitStats = shardStats .getCommitStats ();
217+ if (commitStats == null ) {
218+ onFailure .accept (new IllegalArgumentException ("leader index's commit stats are missing" ));
219+ return ;
220+ }
221+ String historyUUID = commitStats .getUserData ().get (Engine .HISTORY_UUID_KEY );
222+ ShardId shardId = shardStats .getShardRouting ().shardId ();
223+ historyUUIDs [shardId .id ()] = historyUUID ;
224+ }
225+ }
226+ for (int i = 0 ; i < historyUUIDs .length ; i ++) {
227+ if (historyUUIDs [i ] == null ) {
228+ onFailure .accept (new IllegalArgumentException ("no history uuid for [" + leaderIndex + "][" + i + "]" ));
229+ return ;
230+ }
231+ }
232+ historyUUIDConsumer .accept (historyUUIDs );
233+ };
234+ IndicesStatsRequest request = new IndicesStatsRequest ();
235+ request .clear ();
236+ request .indices (leaderIndex );
237+ leaderClient .admin ().indices ().stats (request , ActionListener .wrap (indicesStatsHandler , onFailure ));
238+ }
239+
171240 private static ElasticsearchStatusException indexMetadataNonCompliantRemoteLicense (
172241 final String leaderIndex , final RemoteClusterLicenseChecker .LicenseCheck licenseCheck ) {
173242 final String clusterAlias = licenseCheck .remoteClusterLicenseInfo ().clusterAlias ();
0 commit comments