|
20 | 20 | import org.elasticsearch.action.admin.indices.create.CreateIndexRequest; |
21 | 21 | import org.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder; |
22 | 22 | import org.elasticsearch.action.admin.indices.create.CreateIndexResponse; |
| 23 | +import org.elasticsearch.action.admin.indices.rollover.RolloverRequest; |
23 | 24 | import org.elasticsearch.action.admin.indices.template.put.TransportPutComposableIndexTemplateAction; |
24 | 25 | import org.elasticsearch.action.support.ActiveShardCount; |
25 | 26 | import org.elasticsearch.action.support.IndicesOptions; |
|
33 | 34 | import org.elasticsearch.index.Index; |
34 | 35 | import org.elasticsearch.index.IndexVersion; |
35 | 36 | import org.elasticsearch.index.IndexVersions; |
| 37 | +import org.elasticsearch.index.query.QueryBuilders; |
36 | 38 | import org.elasticsearch.indices.SystemIndexDescriptor; |
37 | 39 | import org.elasticsearch.xcontent.XContentParserConfiguration; |
38 | 40 | import org.elasticsearch.xcontent.json.JsonXContent; |
| 41 | +import org.elasticsearch.xpack.core.ml.job.config.Job; |
| 42 | +import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndex; |
| 43 | +import org.elasticsearch.xpack.core.ml.job.persistence.AnomalyDetectorsIndexFields; |
39 | 44 | import org.elasticsearch.xpack.core.template.IndexTemplateConfig; |
40 | 45 |
|
41 | 46 | import java.io.IOException; |
@@ -504,4 +509,88 @@ public static String latestIndexMatchingBaseName( |
504 | 509 |
|
505 | 510 | return MlIndexAndAlias.latestIndex(filtered); |
506 | 511 | } |
| 512 | + |
| 513 | + public static void rollover(Client client, RolloverRequest rolloverRequest, ActionListener<String> listener) { |
| 514 | + client.admin() |
| 515 | + .indices() |
| 516 | + .rolloverIndex(rolloverRequest, ActionListener.wrap(response -> listener.onResponse(response.getNewIndex()), e -> { |
| 517 | + if (e instanceof ResourceAlreadyExistsException alreadyExistsException) { |
| 518 | + // The destination index already exists possibly because it has been rolled over already. |
| 519 | + listener.onResponse(alreadyExistsException.getIndex().getName()); |
| 520 | + } else { |
| 521 | + listener.onFailure(e); |
| 522 | + } |
| 523 | + })); |
| 524 | + } |
| 525 | + |
| 526 | + public static void createAliasForRollover( |
| 527 | + org.elasticsearch.logging.Logger logger, |
| 528 | + Client client, |
| 529 | + String indexName, |
| 530 | + String aliasName, |
| 531 | + ActionListener<IndicesAliasesResponse> listener |
| 532 | + ) { |
| 533 | + logger.warn("creating rollover [{}] alias for [{}]", aliasName, indexName); |
| 534 | + client.admin() |
| 535 | + .indices() |
| 536 | + .prepareAliases(TimeValue.THIRTY_SECONDS, TimeValue.THIRTY_SECONDS) |
| 537 | + .addAliasAction(IndicesAliasesRequest.AliasActions.add().index(indexName).alias(aliasName).isHidden(true)) |
| 538 | + .execute(listener); |
| 539 | + } |
| 540 | + |
| 541 | + public static void updateAliases(IndicesAliasesRequestBuilder request, ActionListener<Boolean> listener) { |
| 542 | + request.execute(listener.delegateFailure((l, response) -> l.onResponse(Boolean.TRUE))); |
| 543 | + } |
| 544 | + |
| 545 | + public static IndicesAliasesRequestBuilder addIndexAliasesRequests( |
| 546 | + IndicesAliasesRequestBuilder aliasRequestBuilder, |
| 547 | + String oldIndex, |
| 548 | + String newIndex, |
| 549 | + ClusterState clusterState |
| 550 | + ) { |
| 551 | + // Multiple jobs can share the same index each job |
| 552 | + // has a read and write alias that needs updating |
| 553 | + // after the rollover |
| 554 | + var meta = clusterState.metadata().getProject().index(oldIndex); |
| 555 | + assert meta != null; |
| 556 | + if (meta == null) { |
| 557 | + return aliasRequestBuilder; |
| 558 | + } |
| 559 | + |
| 560 | + for (var alias : meta.getAliases().values()) { |
| 561 | + if (isAnomaliesWriteAlias(alias.alias())) { |
| 562 | + aliasRequestBuilder.addAliasAction( |
| 563 | + IndicesAliasesRequest.AliasActions.add().index(newIndex).alias(alias.alias()).isHidden(true).writeIndex(true) |
| 564 | + ); |
| 565 | + aliasRequestBuilder.addAliasAction(IndicesAliasesRequest.AliasActions.remove().index(oldIndex).alias(alias.alias())); |
| 566 | + } else if (isAnomaliesReadAlias(alias.alias())) { |
| 567 | + String jobId = AnomalyDetectorsIndex.jobIdFromAlias(alias.alias()); |
| 568 | + aliasRequestBuilder.addAliasAction( |
| 569 | + IndicesAliasesRequest.AliasActions.add() |
| 570 | + .index(newIndex) |
| 571 | + .alias(alias.alias()) |
| 572 | + .isHidden(true) |
| 573 | + .filter(QueryBuilders.termQuery(Job.ID.getPreferredName(), jobId)) |
| 574 | + ); |
| 575 | + } |
| 576 | + } |
| 577 | + |
| 578 | + return aliasRequestBuilder; |
| 579 | + } |
| 580 | + |
| 581 | + public static boolean isAnomaliesWriteAlias(String aliasName) { |
| 582 | + return aliasName.startsWith(AnomalyDetectorsIndexFields.RESULTS_INDEX_WRITE_PREFIX); |
| 583 | + } |
| 584 | + |
| 585 | + public static boolean isAnomaliesReadAlias(String aliasName) { |
| 586 | + if (aliasName.startsWith(AnomalyDetectorsIndexFields.RESULTS_INDEX_PREFIX) == false) { |
| 587 | + return false; |
| 588 | + } |
| 589 | + |
| 590 | + // See {@link AnomalyDetectorsIndex#jobResultsAliasedName} |
| 591 | + String jobIdPart = aliasName.substring(AnomalyDetectorsIndexFields.RESULTS_INDEX_PREFIX.length()); |
| 592 | + // If this is a write alias it will start with a `.` character |
| 593 | + // which is not a valid job id. |
| 594 | + return MlStrings.isValidId(jobIdPart); |
| 595 | + } |
507 | 596 | } |
0 commit comments