Skip to content

Instantly share code, notes, and snippets.

@dobrinov
Last active January 15, 2018 22:12
Show Gist options
  • Save dobrinov/65a8598a79307b2f937753baf69feadf to your computer and use it in GitHub Desktop.
Save dobrinov/65a8598a79307b2f937753baf69feadf to your computer and use it in GitHub Desktop.
Applying zero downtime Elasticsearch index mapping changes (Ruby on Rails)

This gist assums that you are running a Ruby on Rails project and you are using the elasticsearch-rails gem to operate with your Elasticsearch cluster.

This script is using the Elasticsearch Index Aliases and Reindex API to provide a way for applying zero downtime index mapping changes (adding, deleting or modifying a field).

How it works?

Let's say that you have an Article model which by default references an articles index in Elasticsearch. And lets assume that articles is an alias to a timestamped index articles_20180101000001.

Running the script will:

  1. Create a new timestamped index articles_20180101000002
  2. Will use the Reindexing API to copy all documents from articles_20180101000001 to articles_20180101000002
  3. Will alias articles_20180101000002 to articles.
  4. Will delete articles_20180101000001
model = Article
elasticsearch = Elasticsearch::Client.new host: 'http://localhost:9200',
transport_options: {
request: {open_timeout: 30}
},
logger: Rails.logger
alias_name = model.index_name
alias_exists = elasticsearch.indices.exists_alias? name: alias_name
aliased_index_names =
if alias_exists
elasticsearch.indices.get_alias(name: alias_name).map { |index_name, _| index_name }
else
[]
end
old_index_name = aliased_index_names.first
new_index_name = "#{model.__elasticsearch__.index_name}_#{Time.current.strftime('%Y%m%d%H%M%S')}"
raise 'More than one aliased indexes exist' if aliased_index_names.many?
elasticsearch.indices.create index: new_index_name,
body: {
settings: model.settings.to_hash,
mappings: model.mappings.to_hash,
}
if old_index_name.present?
elasticsearch.reindex body: {
source: {index: old_index_name},
dest: {index: new_index_name}
}
else
model.import index: new_index_name
end
if alias_exists
elasticsearch.indices.update_aliases body: {
actions: [
{remove: {index: old_index_name, alias: alias_name}},
{add: {index: new_index_name, alias: alias_name}},
]
}
else
elasticsearch.indices.put_alias index: new_index_name, name: alias_name
end
elasticsearch.indices.delete index: old_index_name
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment