Allocation model
The existing-shards allocator intentionally does not look for old local shard data. All data is in object store, so the normal shards allocator can assign the shard from scratch. A separate decider enforces that promotable shards land on index nodes and searchable shards land on search nodes.
return (shardRouting.isPromotableToPrimary() && nodeRoles.contains(INDEX_ROLE))
|| (shardRouting.isSearchable() && nodeRoles.contains(SEARCH_ROLE));
Primary relocation source
Primary relocation first performs a best-effort flush, acquires primary context by blocking operations, and then either hollows the source engine or performs a final flush. The source waits for handoff while commit service tracks which generations are safe.
preFlushEngine.flush(false, false, l);
indexShard.relocated(..., (primaryContext, handoffResultListener) -> {
...
indexShard.resetEngine(newEngine -> {
newEngine.refresh("hollowing");
});
});
Relocation target
The target pre-recovers, applies retention leases, skips traditional translog recovery, opens the engine, validates sequence-number consistency, marks file details complete, and activates with the primary context.
indexShard.openEngineAndSkipTranslogRecovery();
...
recoveryState.setStage(RecoveryState.Stage.FINALIZE);
indexShard.activateWithPrimaryContext(request.primaryContext());
Hollow shard blocker
Hollow shards block ingestion with a per-shard listener. A mutable operation either waits on the blocker or initiates unhollowing under the right primary permit conditions. This keeps inactive indexing shards light without letting writes pass through stale hollow state.
var ingestionBlocker = hollowShards.get(shardId);
if (featureEnabled && ingestionBlocker != null) {
ingestionBlocker.listener.addListener(listener);
unhollow(shardId);
}