Change a mapping with zero downtime (reindex + atomic alias switch)
ElasticsearchIndex LifecycleCommand
# 1) Target index with the new mapping (refresh off and replicas 0 for loading):
PUT musteri-v2
{
"settings": { "number_of_shards": 1, "number_of_replicas": 0, "refresh_interval": "-1" },
"mappings": { "properties": { "kod": { "type": "keyword" }, "adet": { "type": "long" } } }
}
# 2) Reindex in the background (tracked with the returned task id):
POST _reindex?wait_for_completion=false&slices=auto
{
"source": { "index": "musteri-v1", "size": 1000 },
"dest": { "index": "musteri-v2", "op_type": "create" },
"conflicts": "proceed"
}
GET _tasks/<task_id>
# 3) Restore settings and compare document counts:
PUT musteri-v2/_settings
{
"index": { "refresh_interval": null, "number_of_replicas": 1 }
}
POST musteri-v2/_refresh
GET _cat/indices/musteri-v1,musteri-v2?v&h=index,docs.count,pri.store.size
# 4) Switch the alias to the new index in one atomic operation:
POST _aliases
{
"actions": [
{ "remove": { "index": "musteri-v1", "alias": "musteri" } },
{ "add": { "index": "musteri-v2", "alias": "musteri", "is_write_index": true } }
]
}What it does
Creates an index with the new mapping, copies the data in the background with reindex, compares document counts, and switches the alias used by the application to the new index in one atomic operation.
When to use it
When an existing field's type must change (text -> keyword, integer -> long), an analyzer must change or the shard count must be redefined; these cannot be done in place on the existing index.
Caution
The application must connect to the alias, not the index name; otherwise the switch needs a deploy. New writes to the source during the reindex do not reach the target: stop writes, or rerun the reindex with op_type create + conflicts proceed just before the switch to close the gap (updates and deletes are not carried over this way). Reindex creates read load on the source and write load on the target; throttle with requests_per_second.