elasticsearch-indexstager 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ee43a625d4aa2fb58e9217e24c8fad74a46650b0
4
+ data.tar.gz: e3c035fb1260b752efcdbe606dd06083c7a9c3e9
5
+ SHA512:
6
+ metadata.gz: 90ed4e107ada5446a21e56b937b83130dddc28e34f1dd49a54f7178437efb764bfa30c3963e48ebd647f0cbcb95dbcf6745f0194e4937db53184c6e4a75ea355
7
+ data.tar.gz: 33c25980044d506eefa1c1a49036e5311911bf9b45900c51e1145965918ffd6f90de061f4964e821db89a7470c9fba06cbd421fcc7788d639883502b45e21095
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,15 @@
1
+ ## Welcome!
2
+
3
+ We're so glad you're thinking about contributing to an 18F open source project! If you're unsure or afraid of anything, just ask or submit the issue or pull request anyways. The worst that can happen is that you'll be politely asked to change something. We appreciate any sort of contribution, and don't want a wall of rules to get in the way of that.
4
+
5
+ Before contributing, we encourage you to read our CONTRIBUTING policy (you are here), our LICENSE, and our README, all of which should be in this repository. If you have any questions, or want to read more about our underlying policies, you can consult the 18F Open Source Policy GitHub repository at https://github.com/18f/open-source-policy, or just shoot us an email/official government letterhead note to [18f@gsa.gov](mailto:18f@gsa.gov).
6
+
7
+ ## Public domain
8
+
9
+ This project is in the public domain within the United States, and
10
+ copyright and related rights in the work worldwide are waived through
11
+ the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/).
12
+
13
+ All contributions to this project will be released under the CC0
14
+ dedication. By submitting a pull request, you are agreeing to comply
15
+ with this waiver of copyright interest.
data/LICENSE.md ADDED
@@ -0,0 +1,31 @@
1
+ As a work of the United States Government, this project is in the
2
+ public domain within the United States.
3
+
4
+ Additionally, we waive copyright and related rights in the work
5
+ worldwide through the CC0 1.0 Universal public domain dedication.
6
+
7
+ ## CC0 1.0 Universal Summary
8
+
9
+ This is a human-readable summary of the [Legal Code (read the full text)](https://creativecommons.org/publicdomain/zero/1.0/legalcode).
10
+
11
+ ### No Copyright
12
+
13
+ The person who associated a work with this deed has dedicated the work to
14
+ the public domain by waiving all of his or her rights to the work worldwide
15
+ under copyright law, including all related and neighboring rights, to the
16
+ extent allowed by law.
17
+
18
+ You can copy, modify, distribute and perform the work, even for commercial
19
+ purposes, all without asking permission.
20
+
21
+ ### Other Information
22
+
23
+ In no way are the patent or trademark rights of any person affected by CC0,
24
+ nor are the rights that other persons may have in the work or in how the
25
+ work is used, such as publicity or privacy rights.
26
+
27
+ Unless expressly stated otherwise, the person who associated a work with
28
+ this deed makes no warranties about the work, and disclaims liability for
29
+ all uses of the work, to the fullest extent permitted by applicable law.
30
+ When using or citing the work, you should not imply endorsement by the
31
+ author or the affirmer.
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # elasticsearch-indexstager RubyGem
2
+
3
+ [![Build Status](https://travis-ci.org/18F/elasticsearch-indexstager-gem.svg?branch=master)](https://travis-ci.org/18F/elasticsearch-indexstager-gem)
4
+
5
+ Elasticsearch index management, for stage/promote pattern.
6
+
7
+ See also:
8
+
9
+ * [elasticsearch-rails-ha](https://github.com/18F/elasticsearch-rails-ha)
10
+
11
+ ## Examples
12
+
13
+ A busy ES installation needs to stay up and serving requests. If you need to build a new index,
14
+ you can "stage" it alongside the live index, and then "promote" the stage to be live. This allows
15
+ for zero downtime cutovers of new indices.
16
+
17
+ ```ruby
18
+ require 'elasticsearch'
19
+ require 'elasticsearch/index_stager'
20
+
21
+ client = Elasticsearch::Client.new log: true
22
+ stager = Elasticsearch::IndexStager.new(index_name: 'foo', es_client: client)
23
+
24
+ client.index(index: stager.tmp_index_name, type: 'article', id: 1, body: { title: 'Test' })
25
+ stager.alias_stage_to_tmp_index
26
+
27
+ results = client.search(index: stager.stage_index_name, body: { query: { match: { title: 'test' } } })
28
+ stager.promote
29
+ results = client.search(index: stager.index_name, body: { query: { match: { title: 'test' } } })
30
+ ```
31
+
32
+ ## Public domain
33
+
34
+ This project is in the worldwide [public domain](LICENSE.md). As stated in [CONTRIBUTING](CONTRIBUTING.md):
35
+
36
+ > This project is in the public domain within the United States, and copyright and related rights in the work worldwide are waived through the [CC0 1.0 Universal public domain dedication](https://creativecommons.org/publicdomain/zero/1.0/).
37
+ >
38
+ > All contributions to this project will be released under the CC0
39
+ > dedication. By submitting a pull request, you are agreeing to comply
40
+ > with this waiver of copyright interest.
@@ -0,0 +1,100 @@
1
+ module Elasticsearch
2
+ class IndexStager
3
+ VERSION = '1.0.0'
4
+
5
+ attr_reader :index_name, :es_client
6
+
7
+ def initialize(opts)
8
+ @index_name = opts[:index_name] or fail ":index_name required"
9
+ @es_client = opts[:es_client] or fail ":es_client required"
10
+ end
11
+
12
+ def stage_index_name
13
+ index_name + "_staged"
14
+ end
15
+
16
+ def tmp_index_name
17
+ @_suffix ||= Time.now.strftime('%Y%m%d%H%M%S') + '-' + SecureRandom.hex[0..7]
18
+ "#{index_name}_#{@_suffix}"
19
+ end
20
+
21
+ def alias_stage_to_tmp_index
22
+ es_client.indices.delete index: stage_index_name rescue false
23
+ es_client.indices.update_aliases body: {
24
+ actions: [
25
+ { add: { index: tmp_index_name, alias: stage_index_name } }
26
+ ]
27
+ }
28
+ end
29
+
30
+ def promote(live_index_name=index_name)
31
+ @live_index_name = live_index_name || index_name
32
+
33
+ # the renaming actions (performed atomically by ES)
34
+ rename_actions = [
35
+ { remove: { index: stage_aliased_to, alias: stage_index_name } },
36
+ { add: { index: stage_index_name, alias: @live_index_name } }
37
+ ]
38
+
39
+ # zap any existing index known as index_name,
40
+ # but do it conditionally since it is reasonable that it does not exist.
41
+ to_delete = []
42
+ existing_live_index = es_client.indices.get_aliases(index: @live_index_name)
43
+ existing_live_index.each do |k,v|
44
+
45
+ # if the index is merely aliased, remove its alias as part of the aliasing transaction.
46
+ if k != @live_index_name
47
+ rename_actions.unshift({ remove: { index: k, alias: @live_index_name } })
48
+
49
+ # mark it for deletion when we've successfully updated aliases
50
+ to_delete.push k
51
+
52
+ else
53
+ # this is a real, unaliased index with this name, so it must be deleted.
54
+ # (This usually happens the first time we implement the aliasing scheme against
55
+ # an existing installation.)
56
+ es_client.indices.delete index: @live_index_name rescue false
57
+ end
58
+ end
59
+
60
+ # re-alias
61
+ es_client.indices.update_aliases body: { actions: rename_actions }
62
+
63
+ # clean up
64
+ to_delete.each do |idxname|
65
+ es_client.indices.delete index: idxname rescue false
66
+ end
67
+ end
68
+
69
+ private
70
+
71
+ def tmp_index_pattern
72
+ /#{index_name}_(\d{14})-\w{8}$/
73
+ end
74
+
75
+ def stage_aliased_to
76
+ # find the newest tmp index to which staged is aliased.
77
+ # we need this because we want to re-alias it.
78
+ aliased_to = find_newest_alias_for(stage_index_name)
79
+ end
80
+
81
+ def find_newest_alias_for(the_index_name)
82
+ aliased_to = nil
83
+ aliases = es_client.indices.get_aliases(index: the_index_name)
84
+ aliases.each do |k,v|
85
+ next unless k.match(tmp_index_pattern)
86
+ aliased_to ||= k
87
+ alias_tstamp = aliased_to.match(tmp_index_pattern)[1]
88
+ k_tstamp = k.match(tmp_index_pattern)[1]
89
+ if Time.parse(alias_tstamp) < Time.parse(k_tstamp)
90
+ aliased_to = k
91
+ end
92
+ end
93
+ if !aliased_to
94
+ raise "Cannot identify index aliased to by '#{the_index_name}'"
95
+ end
96
+ aliased_to
97
+ end
98
+
99
+ end
100
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: elasticsearch-indexstager
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter Karman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: elasticsearch
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: about_yml
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: elasticsearch-extensions
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: manage Elasticsearch indexes using staging pattern
98
+ email:
99
+ - peter.karman@gsa.gov
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - CONTRIBUTING.md
105
+ - LICENSE.md
106
+ - README.md
107
+ - lib/elasticsearch/index_stager.rb
108
+ homepage: https://github.com/18F/elasticsearch-indexstager-gem
109
+ licenses:
110
+ - CC0
111
+ metadata: {}
112
+ post_install_message:
113
+ rdoc_options: []
114
+ require_paths:
115
+ - lib
116
+ required_ruby_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: 1.9.3
121
+ required_rubygems_version: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 2.4.5.1
129
+ signing_key:
130
+ specification_version: 4
131
+ summary: manage Elasticsearch indexes using staging pattern
132
+ test_files: []
133
+ has_rdoc: