Skip to content
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
83b803c
Restore live index settings on per-entity distributed-promote path
harshach May 4, 2026
ba952d9
Wire jobData into per-entity reindex promotion handler
harshach May 4, 2026
80f01f6
Add regression test for live serving settings on per-entity promote
harshach May 5, 2026
c373592
Expand unit coverage around the per-entity promotion contract
harshach May 5, 2026
20e1cd4
Add integration test for live settings restoration after alias promotion
harshach May 5, 2026
a977032
Address PR review: harden settings revert + lock InOrder + drop redun…
harshach May 5, 2026
a2a2b4a
Drop verbose explanatory comments from promote-path edits
harshach May 5, 2026
f40a21b
Close Rest5Client in IT _settings helper
harshach May 5, 2026
4775e90
Tighten SearchIndexAliasPromotionIT against false-positive runs
harshach May 5, 2026
2af5827
Harden alias promotion: defer canonical delete, hard-fail on empty al…
harshach May 5, 2026
4654158
Consolidate finalizeReindex and promoteEntityIndex into one core path
harshach May 5, 2026
9a7fa49
Address PR review: post-state checks, FAILED listener, hermetic IT, I…
harshach May 6, 2026
8b4d1a8
Wrap post-state checks: indexExists / getAliases throws no longer escape
harshach May 6, 2026
2580597
Address Copilot review 4232747647: positive-evidence dataLoss, hermet…
harshach May 6, 2026
0351078
Merge branch 'main' into harshach/search-alias-promote
harshach May 6, 2026
30774c7
Wait for restore-triggered run to settle in SearchIndexAliasPromotionIT
harshach May 6, 2026
232d195
Fix AppsResourceIT.waitForAppJobCompletion case mismatch and timeout
harshach May 6, 2026
22717f5
Merge branch 'main' into harshach/search-alias-promote
mohityadav766 May 6, 2026
98b9871
Merge remote-tracking branch 'origin/main' into harshach/search-alias…
harshach May 6, 2026
aee61f2
Merge branch 'harshach/search-alias-promote' of github.com:open-metad…
harshach May 6, 2026
64a385a
Run SearchIndexAliasPromotionIT in the sequential bucket
harshach May 6, 2026
810ed16
Address Copilot PR review 4233452655
harshach May 6, 2026
cf18270
Remove SearchIndexAliasPromotionIT in favor of unit test coverage
harshach May 6, 2026
7d7e5db
Address Copilot PR review 4236718653
harshach May 6, 2026
bb973d8
Fix per-entity promote when canonical is an alias, not a concrete index
harshach May 6, 2026
0164cd8
Add ALIAS_PROMOTE_BEGIN diagnostic log per entity
harshach May 6, 2026
d80e44b
Drop heavy alias-promotion refactor; rely on PR #27930 fix already in…
harshach May 6, 2026
3e0d7ca
Merge remote-tracking branch 'origin/main' into harshach/search-alias…
harshach May 6, 2026
25240ec
Skip delete-by-alias-name when canonical is currently an alias
harshach May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,15 @@ static void setup() {
private void waitForAppJobCompletion(String appName) {
HttpClient httpClient = SdkClients.adminClient().getHttpClient();
try {
// AppRunRecord.status is a lowercase enum (see appRunRecord.json: started, running,
// completed, failed, success, activeError, stopped, ...). Comparing with case-insensitive
// matchers — using uppercase here matches none of the real values and silently makes the
// wait a no-op. 5-minute ceiling covers an in-flight reindex from another test class
// (e.g. SearchIndexingFieldsParityIT triggers an "all entities" reindex that can take
// minutes); a 30s ceiling fell through to the catch and let the trigger Awaitility below
// hit its own 2-minute "already running" wall.
Awaitility.await("Wait for app job completion: " + appName)
.atMost(Duration.ofSeconds(30))
.atMost(Duration.ofMinutes(5))
.pollDelay(Duration.ofMillis(500))
.pollInterval(Duration.ofSeconds(2))
.ignoreExceptions()
Expand All @@ -98,9 +105,7 @@ private void waitForAppJobCompletion(String appName) {
return true;
}
String status = latestRun.getStatus().value();
return "SUCCESS".equals(status)
|| "FAILED".equals(status)
|| "COMPLETED".equals(status);
return !"running".equalsIgnoreCase(status) && !"started".equalsIgnoreCase(status);
});
} catch (org.awaitility.core.ConditionTimeoutException e) {
// Best-effort wait — the app may be continuously running under parallel test load.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,10 +185,20 @@ private ExecutionResult doExecute(
recreateContext,
!stopped.get() && !hasIncompleteProcessing(stats));

ExecutionResult.Status resultStatus = determineStatus(stats);
ExecutionResult.Status resultStatus = determineStatus(stats, recreateIndexHandler);

StatsReconciler.reconcile(stats);

if (resultStatus == ExecutionResult.Status.FAILED) {
Set<String> dataLoss = collectDataLossPromotions(recreateIndexHandler);
listeners.onJobFailed(
stats,
new IllegalStateException(
"Promotion data loss for entities: "
+ dataLoss
+ ". Canonical index was deleted but alias not re-attached."));
}

return ExecutionResult.builder()
.status(resultStatus)
.totalRecords(stats.getJobStats().getTotalRecords())
Expand All @@ -201,6 +211,19 @@ private ExecutionResult doExecute(
.build();
}

private Set<String> collectDataLossPromotions(RecreateIndexHandler strategyHandler) {
Set<String> all = new HashSet<>();
if (strategyHandler != null) {
all.addAll(strategyHandler.getDataLossPromotions());
}
RecreateIndexHandler executorHandler =
distributedExecutor != null ? distributedExecutor.getRecreateIndexHandler() : null;
if (executorHandler != null) {
all.addAll(executorHandler.getDataLossPromotions());
}
return all;
}

private void flushAndAwaitSink() {
if (searchIndexSink == null) {
return;
Expand Down Expand Up @@ -452,11 +475,15 @@ private static int saturatedToInt(long value) {
return (int) Math.min(value, Integer.MAX_VALUE);
}

private ExecutionResult.Status determineStatus(Stats stats) {
private ExecutionResult.Status determineStatus(
Stats stats, RecreateIndexHandler strategyHandler) {
if (stopped.get()) {
return ExecutionResult.Status.STOPPED;
}
if (hasIncompleteProcessing(stats)) {
if (hasDataLossPromotions(strategyHandler)) {
return ExecutionResult.Status.FAILED;
}
if (hasIncompleteProcessing(stats) || hasFailedPromotions(strategyHandler)) {
Comment thread
harshach marked this conversation as resolved.
Outdated
return ExecutionResult.Status.COMPLETED_WITH_ERRORS;
Comment thread
harshach marked this conversation as resolved.
Outdated
}
return ExecutionResult.Status.COMPLETED;
Expand All @@ -473,6 +500,24 @@ private boolean hasIncompleteProcessing(Stats stats) {
return failed > 0 || (total > 0 && processed < total);
}

private boolean hasFailedPromotions(RecreateIndexHandler strategyHandler) {
if (strategyHandler != null && !strategyHandler.getFailedPromotions().isEmpty()) {
return true;
}
RecreateIndexHandler executorHandler =
distributedExecutor != null ? distributedExecutor.getRecreateIndexHandler() : null;
return executorHandler != null && !executorHandler.getFailedPromotions().isEmpty();
}

private boolean hasDataLossPromotions(RecreateIndexHandler strategyHandler) {
if (strategyHandler != null && !strategyHandler.getDataLossPromotions().isEmpty()) {
return true;
}
RecreateIndexHandler executorHandler =
distributedExecutor != null ? distributedExecutor.getRecreateIndexHandler() : null;
return executorHandler != null && !executorHandler.getDataLossPromotions().isEmpty();
}

private boolean finalizeAllEntityReindex(
RecreateIndexHandler recreateIndexHandler,
ReindexContext recreateContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1762,6 +1762,15 @@ private ExecutionResult buildResult() {
listeners.onJobCompletedWithErrors(stats.get(), endTime - startTime);
} else if (status == ExecutionResult.Status.STOPPED) {
listeners.onJobStopped(stats.get());
} else if (status == ExecutionResult.Status.FAILED) {
Set<String> dataLoss =
recreateIndexHandler != null ? recreateIndexHandler.getDataLossPromotions() : Set.of();
listeners.onJobFailed(
stats.get(),
new IllegalStateException(
"Promotion data loss for entities: "
+ dataLoss
+ ". Canonical index was deleted but alias not re-attached."));
}

return ExecutionResult.fromStats(stats.get(), status, startTime);
Expand All @@ -1771,26 +1780,35 @@ private ExecutionResult.Status determineStatus() {
if (stopped.get()) {
return ExecutionResult.Status.STOPPED;
}

if (hasDataLossPromotions()) {
return ExecutionResult.Status.FAILED;
Comment thread
harshach marked this conversation as resolved.
Outdated
}
if (hasIncompleteProcessing()) {
return ExecutionResult.Status.COMPLETED_WITH_ERRORS;
}

return ExecutionResult.Status.COMPLETED;
}

private boolean hasIncompleteProcessing() {
Stats currentStats = stats.get();
if (currentStats == null || currentStats.getJobStats() == null) {
return false;
return hasFailedPromotions();
}

StepStats jobStats = currentStats.getJobStats();
long failed = jobStats.getFailedRecords() != null ? jobStats.getFailedRecords() : 0;
long processed = jobStats.getSuccessRecords() != null ? jobStats.getSuccessRecords() : 0;
long total = jobStats.getTotalRecords() != null ? jobStats.getTotalRecords() : 0;

return failed > 0 || (total > 0 && processed < total);
return failed > 0 || (total > 0 && processed < total) || hasFailedPromotions();
}

private boolean hasFailedPromotions() {
return recreateIndexHandler != null && !recreateIndexHandler.getFailedPromotions().isEmpty();
}

private boolean hasDataLossPromotions() {
return recreateIndexHandler != null && !recreateIndexHandler.getDataLossPromotions().isEmpty();
}

public void stop() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ public static boolean isCoordinatingJob(UUID jobId) {

// Per-entity index promotion
private EntityCompletionTracker entityTracker;
private RecreateIndexHandler recreateIndexHandler;
@Getter private RecreateIndexHandler recreateIndexHandler;
private ReindexContext recreateContext;

// Reader stats tracking (accumulated across all worker threads)
Expand Down Expand Up @@ -1099,7 +1099,6 @@ private void initializeEntityTracker(UUID jobId, boolean recreateIndex) {
partitionCountByEntity.size(),
partitionCountByEntity);

// Set up per-entity promotion callback if recreating indices
if (recreateIndex && recreateContext != null) {
this.recreateIndexHandler = Entity.getSearchRepository().createReindexHandler();
// Wire job configuration so applyLiveServingSettings can revert bulk-build overrides
Expand Down
Loading
Loading