zelastic 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +39 -79
- data/.github/dependabot.yml +9 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +10 -21
- data/CHANGELOG.md +41 -9
- data/Gemfile +2 -0
- data/README.md +6 -3
- data/bin/rspec +27 -0
- data/bin/rubocop +27 -0
- data/lib/zelastic/config.rb +14 -13
- data/lib/zelastic/index_manager.rb +22 -27
- data/lib/zelastic/indexer.rb +21 -34
- data/lib/zelastic/version.rb +1 -1
- data/log/.keep +0 -0
- data/zelastic.gemspec +8 -7
- metadata +47 -39
- data/.rubocop_todo.yml +0 -32
- data/Gemfile.lock +0 -91
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7b45ad7dd15f213cc571c3da4f021ff80129dcccb375ae98aedde7b22b759b05
|
4
|
+
data.tar.gz: '08328c62f6485a0b11300dc9c651ca3e8c99179c84ea2c3f5235a921ca676d9a'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a3e38047bf9f47bc8e7353f8e52387a7bd6722e291f96291d09a50257cd28a43cf7fba2abcd9681abb3797aa5acce67f38b09cabb6ecbaf8e4638b2a7e6e21b6
|
7
|
+
data.tar.gz: 94572581d7fa5f6c6de6485cacd9232a310418379f981d12d0523183f6922f5bcbbeb290605e4452ccb51603f702aa5bf52a9540ed681002a1b2e389a2dff670
|
data/.circleci/config.yml
CHANGED
@@ -1,88 +1,48 @@
|
|
1
|
-
|
2
|
-
ruby: &ruby
|
3
|
-
image: carwow/ruby-ci:2.4.4
|
4
|
-
environment:
|
5
|
-
ELASTICSEARCH_URL: http://localhost:9200
|
6
|
-
|
7
|
-
elasticsearch_container: &elasticsearch_container
|
8
|
-
image: carwow/elasticsearch-ci:5.5.1
|
1
|
+
version: 2.1
|
9
2
|
|
10
|
-
elasticsearch_7_container: &elasticsearch_7_container
|
11
|
-
image: carwow/elasticsearch-ci:7.6.1
|
12
|
-
|
13
|
-
version: 2
|
14
3
|
jobs:
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
- bundle-{{ checksum "Gemfile.lock" }}
|
24
|
-
- bundle-
|
25
|
-
- run: |
|
26
|
-
bundle config --local path vendor/bundle &&
|
27
|
-
bundle check || bundle install --jobs=4 --retry=3
|
28
|
-
bundle clean --force
|
29
|
-
- save_cache:
|
30
|
-
key: bundle-{{ checksum "Gemfile.lock" }}
|
31
|
-
paths: [vendor/bundle]
|
32
|
-
|
33
|
-
rubocop:
|
34
|
-
working_directory: ~/zelastic
|
35
|
-
docker:
|
36
|
-
- *ruby
|
37
|
-
steps:
|
38
|
-
- checkout
|
39
|
-
- restore_cache: { keys: ['bundle-{{ checksum "Gemfile.lock" }}'] }
|
40
|
-
- run: bundle exec rubocop
|
41
|
-
|
42
|
-
tests:
|
43
|
-
working_directory: ~/zelastic
|
44
|
-
docker:
|
45
|
-
- *ruby
|
46
|
-
- *elasticsearch_container
|
47
|
-
steps:
|
48
|
-
- checkout
|
49
|
-
- restore_cache: { keys: ['bundle-{{ checksum "Gemfile.lock" }}'] }
|
50
|
-
- run:
|
51
|
-
name: Wait for ES to be ready
|
52
|
-
command: |
|
53
|
-
until curl $ELASTICSEARCH_URL/_cat/health | egrep '(green|yellow)' 2>&1 > /dev/null; do
|
54
|
-
echo -n .
|
55
|
-
sleep 1
|
56
|
-
done
|
57
|
-
- run: |
|
58
|
-
bundle exec rspec --pattern "**/*_spec.rb" --format "progress"
|
59
|
-
|
60
|
-
tests_7:
|
61
|
-
working_directory: ~/zelastic
|
4
|
+
test:
|
5
|
+
parameters:
|
6
|
+
ruby_version:
|
7
|
+
type: string
|
8
|
+
elasticsearch_version:
|
9
|
+
type: string
|
10
|
+
elasticsearch_gem_version:
|
11
|
+
type: string
|
62
12
|
docker:
|
63
|
-
-
|
64
|
-
-
|
13
|
+
- image: "cimg/ruby:<< parameters.ruby_version >>"
|
14
|
+
- image: "elasticsearch:<< parameters.elasticsearch_version >>"
|
15
|
+
environment:
|
16
|
+
"discovery.type": single-node
|
17
|
+
"xpack.security.enabled": false
|
18
|
+
environment:
|
19
|
+
ELASTICSEARCH_GEM_VERSION: "<< parameters.elasticsearch_gem_version >>"
|
65
20
|
steps:
|
66
21
|
- checkout
|
67
|
-
-
|
68
|
-
- run:
|
69
|
-
|
70
|
-
|
71
|
-
until curl $ELASTICSEARCH_URL/_cat/health | egrep '(green|yellow)' 2>&1 > /dev/null; do
|
72
|
-
echo -n .
|
73
|
-
sleep 1
|
74
|
-
done
|
75
|
-
- run: |
|
76
|
-
bundle exec rspec --pattern "**/*_spec.rb" --format "progress"
|
22
|
+
- run: bin/setup
|
23
|
+
- run: bin/rubocop
|
24
|
+
- run: dockerize -wait http://localhost:9200 -timeout 1m
|
25
|
+
- run: bin/rspec
|
77
26
|
|
78
27
|
workflows:
|
79
28
|
version: 2
|
80
|
-
|
29
|
+
test:
|
81
30
|
jobs:
|
82
|
-
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
31
|
+
- test:
|
32
|
+
name: "test with elasticsearch v8
|
33
|
+
and gem << matrix.elasticsearch_gem_version >>
|
34
|
+
and ruby v<< matrix.ruby_version >>"
|
35
|
+
elasticsearch_version: "8.3.3"
|
36
|
+
matrix:
|
37
|
+
parameters:
|
38
|
+
ruby_version: ["3.1", "2.7"]
|
39
|
+
elasticsearch_gem_version: ["~> 8", "~> 7"]
|
40
|
+
- test:
|
41
|
+
name: "test with elasticsearch v7
|
42
|
+
and gem << matrix.elasticsearch_gem_version >>
|
43
|
+
and ruby v<< matrix.ruby_version >>"
|
44
|
+
elasticsearch_version: "7.17.5"
|
45
|
+
matrix:
|
46
|
+
parameters:
|
47
|
+
ruby_version: ["3.1", "2.7"]
|
48
|
+
elasticsearch_gem_version: ["~> 7"]
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,24 +1,13 @@
|
|
1
|
-
|
1
|
+
inherit_gem:
|
2
|
+
carwow_rubocop:
|
3
|
+
- config/default.yml
|
2
4
|
|
3
|
-
|
4
|
-
|
5
|
+
inherit_mode:
|
6
|
+
merge:
|
7
|
+
- Exclude
|
5
8
|
|
6
|
-
|
7
|
-
|
8
|
-
Exclude:
|
9
|
-
- '*.gemspec'
|
10
|
-
- 'spec/**/*'
|
9
|
+
Metrics:
|
10
|
+
Enabled: false
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
Max: 18
|
15
|
-
|
16
|
-
# Offense count: 2
|
17
|
-
Style/Documentation:
|
18
|
-
Exclude:
|
19
|
-
- '**/*'
|
20
|
-
|
21
|
-
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
22
|
-
# URISchemes: http, https
|
23
|
-
Metrics/LineLength:
|
24
|
-
Max: 100
|
12
|
+
Gemspec/RequireMFA:
|
13
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,15 +1,47 @@
|
|
1
|
-
|
2
|
-
---
|
1
|
+
# Changelog
|
3
2
|
|
4
|
-
|
3
|
+
All notable changes to this project will be documented in this file.
|
5
4
|
|
6
|
-
|
7
|
-
|
5
|
+
The format is based on [Keep a Changelog],
|
6
|
+
and this project adheres to [Semantic Versioning].
|
8
7
|
|
9
|
-
|
8
|
+
[Keep a Changelog]: https://keepachangelog.com/en/1.0.0/
|
9
|
+
[Semantic Versioning]: https://semver.org/spec/v2.0.0.html
|
10
10
|
|
11
|
+
## How do I make a good changelog?
|
11
12
|
|
12
|
-
|
13
|
-
---
|
13
|
+
### Guiding Principles
|
14
14
|
|
15
|
-
-
|
15
|
+
- Changelogs are for humans, not machines.
|
16
|
+
- There should be an entry for every single version.
|
17
|
+
- The same types of changes should be grouped.
|
18
|
+
- Versions and sections should be linkable.
|
19
|
+
- The latest version comes first.
|
20
|
+
- The release date of each version is displayed.
|
21
|
+
- Keep an `Unreleased` section at the top to track upcoming changes.
|
22
|
+
|
23
|
+
### Types of changes
|
24
|
+
|
25
|
+
- `Added` for new features.
|
26
|
+
- `Changed` for changes in existing functionality.
|
27
|
+
- `Deprecated` for soon-to-be removed features.
|
28
|
+
- `Removed` for now removed features.
|
29
|
+
- `Fixed` for any bug fixes.
|
30
|
+
- `Security` in case of vulnerabilities.
|
31
|
+
|
32
|
+
## [Unreleased] - XXXX-XX-XX
|
33
|
+
|
34
|
+
## [0.8.0] - 2022-08-19
|
35
|
+
### Added
|
36
|
+
- Support for Ruby 3 (and keep support for 2.7).
|
37
|
+
- Support for Elasticsearch v8 (and keep support for v7).
|
38
|
+
- Support setting a logger in `Config`.
|
39
|
+
- Support refresh on `IndexManager#populate_index`.
|
40
|
+
- Support Proc in `Config#data_source` so it can be lazily evaluated.
|
41
|
+
|
42
|
+
### Removed
|
43
|
+
- Drop support for Ruby 2.6.
|
44
|
+
- Drop support for Elasticsearch v5 and v6.
|
45
|
+
|
46
|
+
[Unreleased]: https://github.com/carwow/zelastic/compare/v0.8.0...HEAD
|
47
|
+
[0.8.0]: https://github.com/carwow/zelastic/releases/tag/v0.8.0
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
#
|
1
|
+
# Zelastic
|
2
|
+
|
3
|
+
Zero-downtime Elasticsearch tooling for managing indices and indexing from
|
4
|
+
ActiveRecord with PostgreSQL to Elasticsearch.
|
2
5
|
|
3
6
|
## Installation
|
4
7
|
|
@@ -10,13 +13,14 @@ gem 'zelastic'
|
|
10
13
|
|
11
14
|
And then execute:
|
12
15
|
|
13
|
-
$ bundle
|
16
|
+
$ bundle install
|
14
17
|
|
15
18
|
Or install it yourself as:
|
16
19
|
|
17
20
|
$ gem install zelastic
|
18
21
|
|
19
22
|
## Usage
|
23
|
+
|
20
24
|
### Setup
|
21
25
|
|
22
26
|
For each ActiveRecord scope you want to index, you'll need a configuration:
|
@@ -46,7 +50,6 @@ You can also override some defaults, if you wish:
|
|
46
50
|
here
|
47
51
|
- `read_alias`: by default this is the table name of the `data_source`
|
48
52
|
- `write_alias`: by default this is the `read_alias`, with `_write` appended
|
49
|
-
- `type`: by default this is `read_alias.singularize`
|
50
53
|
|
51
54
|
If you pass an array to as the `client` argument, all writes will be applied to every client in the
|
52
55
|
array.
|
data/bin/rspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rspec' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path('bundle', __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'bundler/setup'
|
26
|
+
|
27
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
data/bin/rubocop
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
#
|
5
|
+
# This file was generated by Bundler.
|
6
|
+
#
|
7
|
+
# The application 'rubocop' is installed as part of a gem, and
|
8
|
+
# this file is here to facilitate running it.
|
9
|
+
#
|
10
|
+
|
11
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
|
12
|
+
|
13
|
+
bundle_binstub = File.expand_path('bundle', __dir__)
|
14
|
+
|
15
|
+
if File.file?(bundle_binstub)
|
16
|
+
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
|
17
|
+
load(bundle_binstub)
|
18
|
+
else
|
19
|
+
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
|
20
|
+
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'rubygems'
|
25
|
+
require 'bundler/setup'
|
26
|
+
|
27
|
+
load Gem.bin_path('rubocop', 'rubocop')
|
data/lib/zelastic/config.rb
CHANGED
@@ -2,31 +2,36 @@
|
|
2
2
|
|
3
3
|
module Zelastic
|
4
4
|
class Config
|
5
|
-
attr_reader :clients
|
5
|
+
attr_reader :clients
|
6
6
|
|
7
7
|
def initialize(
|
8
8
|
client:,
|
9
9
|
data_source:,
|
10
10
|
mapping:,
|
11
|
+
logger: nil,
|
11
12
|
**overrides,
|
12
13
|
&index_data
|
13
14
|
)
|
14
15
|
@clients = Array(client)
|
15
16
|
@data_source = data_source
|
16
17
|
@mapping = mapping
|
17
|
-
@
|
18
|
-
@_type = overrides.fetch(:type, true)
|
18
|
+
@logger = logger
|
19
19
|
@overrides = overrides
|
20
|
-
|
21
|
-
|
22
|
-
def type?
|
23
|
-
@_type
|
20
|
+
@index_data = index_data
|
24
21
|
end
|
25
22
|
|
26
23
|
def index_data(model)
|
27
24
|
@index_data.call(model)
|
28
25
|
end
|
29
26
|
|
27
|
+
def data_source
|
28
|
+
if @data_source.respond_to? :call
|
29
|
+
@data_source.call
|
30
|
+
else
|
31
|
+
@data_source
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
30
35
|
def read_alias
|
31
36
|
@read_alias ||= overrides.fetch(:read_alias) { data_source.table_name }
|
32
37
|
end
|
@@ -35,20 +40,16 @@ module Zelastic
|
|
35
40
|
@write_alias ||= overrides.fetch(:write_alias) { [read_alias, 'write'].join('_') }
|
36
41
|
end
|
37
42
|
|
38
|
-
def type
|
39
|
-
@type ||= overrides.fetch(:type, read_alias.singularize)
|
40
|
-
end
|
41
|
-
|
42
43
|
def logger
|
43
44
|
return Rails.logger if defined?(Rails)
|
44
45
|
|
45
|
-
@logger ||= Logger.new(
|
46
|
+
@logger ||= Logger.new($stdout)
|
46
47
|
end
|
47
48
|
|
48
49
|
def index_definition
|
49
50
|
{
|
50
51
|
settings: overrides.fetch(:index_settings, {}),
|
51
|
-
mappings:
|
52
|
+
mappings: mapping
|
52
53
|
}
|
53
54
|
end
|
54
55
|
|
@@ -1,8 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Zelastic
|
4
|
-
|
5
|
-
class IndexManager # rubocop:disable Metrics/ClassLength
|
4
|
+
class IndexManager
|
6
5
|
extend Forwardable
|
7
6
|
|
8
7
|
def initialize(config, client: nil)
|
@@ -17,12 +16,12 @@ module Zelastic
|
|
17
16
|
client.indices.put_alias(index: index_name, name: config.write_alias)
|
18
17
|
end
|
19
18
|
|
20
|
-
def populate_index(unique_name = nil, batch_size: 3000)
|
19
|
+
def populate_index(unique_name = nil, batch_size: 3000, refresh: false)
|
21
20
|
index_name = index_name_from_unique(unique_name)
|
22
21
|
|
23
22
|
config.data_source.find_in_batches(batch_size: batch_size).with_index do |batch, i|
|
24
23
|
logger.info(populate_index_log(batch_size: batch_size, batch_number: i + 1))
|
25
|
-
indexer.index_batch(batch, client: client, index_name: index_name)
|
24
|
+
indexer.index_batch(batch, client: client, index_name: index_name, refresh: refresh)
|
26
25
|
end
|
27
26
|
end
|
28
27
|
|
@@ -37,12 +36,14 @@ module Zelastic
|
|
37
36
|
remove_action =
|
38
37
|
({ remove: { index: old_index, alias: config.read_alias } } if old_index)
|
39
38
|
|
40
|
-
client.indices.update_aliases(
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
39
|
+
client.indices.update_aliases(
|
40
|
+
body: {
|
41
|
+
actions: [
|
42
|
+
remove_action,
|
43
|
+
{ add: { index: new_index, alias: config.read_alias } }
|
44
|
+
].compact
|
45
|
+
}
|
46
|
+
)
|
46
47
|
end
|
47
48
|
|
48
49
|
def stop_dual_writes
|
@@ -52,14 +53,15 @@ module Zelastic
|
|
52
53
|
logger.info("Currently used index is #{current_index}")
|
53
54
|
|
54
55
|
other_write_indices = client.indices.get_alias(name: config.write_alias).keys
|
55
|
-
|
56
|
+
.reject { |name| name == current_index }
|
56
57
|
|
57
58
|
if other_write_indices.none?
|
58
59
|
logger.info("No write indexes that aren't the read index. Nothing to do!")
|
59
60
|
return
|
60
61
|
end
|
61
62
|
logger.info("Stopping writes to #{other_write_indices.count} old ES indices: " \
|
62
|
-
|
63
|
+
"#{other_write_indices.join(', ')}"
|
64
|
+
)
|
63
65
|
|
64
66
|
actions = other_write_indices.map do |index|
|
65
67
|
{ remove: { index: index, alias: config.write_alias } }
|
@@ -74,11 +76,11 @@ module Zelastic
|
|
74
76
|
logger.info("Currently used index is #{current_index}")
|
75
77
|
|
76
78
|
indices_to_delete = client
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
79
|
+
.cat
|
80
|
+
.indices(format: :json)
|
81
|
+
.map { |index| index['index'] }
|
82
|
+
.select { |name| name.start_with?(config.read_alias) }
|
83
|
+
.reject { |name| name == current_index }
|
82
84
|
|
83
85
|
if indices_to_delete.none?
|
84
86
|
logger.info('Nothing to do: no old indices')
|
@@ -93,6 +95,7 @@ module Zelastic
|
|
93
95
|
private
|
94
96
|
|
95
97
|
attr_reader :config, :client
|
98
|
+
|
96
99
|
def_delegators :config, :logger
|
97
100
|
|
98
101
|
def indexer
|
@@ -113,18 +116,11 @@ module Zelastic
|
|
113
116
|
else
|
114
117
|
'First index'
|
115
118
|
end
|
116
|
-
"ES: (#{progress}) Indexing
|
119
|
+
"ES: (#{progress}) Indexing records"
|
117
120
|
end
|
118
121
|
|
119
122
|
def current_index_size
|
120
|
-
@current_index_size ||= client.count(
|
121
|
-
end
|
122
|
-
|
123
|
-
def count_params
|
124
|
-
{
|
125
|
-
index: config.read_alias,
|
126
|
-
type: config.type? ? config.type : nil
|
127
|
-
}.compact
|
123
|
+
@current_index_size ||= client.count(index: config.read_alias)['count']
|
128
124
|
end
|
129
125
|
|
130
126
|
def indexed_percent(batch_size, batch_number)
|
@@ -135,5 +131,4 @@ module Zelastic
|
|
135
131
|
client.indices.exists?(index: config.read_alias)
|
136
132
|
end
|
137
133
|
end
|
138
|
-
# rubocop:enable Metrics/AbcSize
|
139
134
|
end
|
data/lib/zelastic/indexer.rb
CHANGED
@@ -17,19 +17,19 @@ module Zelastic
|
|
17
17
|
@config = config
|
18
18
|
end
|
19
19
|
|
20
|
-
def index_batch(batch, client: nil, index_name: nil)
|
20
|
+
def index_batch(batch, client: nil, index_name: nil, refresh: false)
|
21
21
|
version = current_version
|
22
|
-
execute_bulk(client: client, index_name: index_name) do |index|
|
22
|
+
execute_bulk(client: client, index_name: index_name, refresh: refresh) do |index|
|
23
23
|
batch.map do |record|
|
24
24
|
index_command(index: index, version: version, record: record)
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def index_record(record)
|
29
|
+
def index_record(record, refresh: false)
|
30
30
|
version = current_version
|
31
31
|
|
32
|
-
execute_bulk do |index_name|
|
32
|
+
execute_bulk(refresh: refresh) do |index_name|
|
33
33
|
[index_command(index: index_name, version: version, record: record)]
|
34
34
|
end
|
35
35
|
end
|
@@ -43,10 +43,7 @@ module Zelastic
|
|
43
43
|
|
44
44
|
execute_bulk do |index_name|
|
45
45
|
ids.map do |id|
|
46
|
-
|
47
|
-
delete_params[:_type] = config.type if config.type?
|
48
|
-
|
49
|
-
{ delete: delete_params }
|
46
|
+
{ delete: { _index: index_name, _id: id } }
|
50
47
|
end
|
51
48
|
end
|
52
49
|
end
|
@@ -62,12 +59,13 @@ module Zelastic
|
|
62
59
|
private
|
63
60
|
|
64
61
|
attr_reader :config
|
62
|
+
|
65
63
|
def_delegators :config, :logger
|
66
64
|
|
67
65
|
def current_version
|
68
66
|
config.data_source.connection
|
69
|
-
|
70
|
-
|
67
|
+
.select_one('SELECT txid_snapshot_xmax(txid_current_snapshot()) as xmax')
|
68
|
+
.fetch('xmax')
|
71
69
|
end
|
72
70
|
|
73
71
|
def write_indices(client)
|
@@ -75,31 +73,26 @@ module Zelastic
|
|
75
73
|
end
|
76
74
|
|
77
75
|
def index_command(index:, version:, record:)
|
78
|
-
version_params =
|
79
|
-
if config.type?
|
80
|
-
{ _version: version, _version_type: :external, _type: config.type }
|
81
|
-
else
|
82
|
-
{ version: version, version_type: :external }
|
83
|
-
end
|
84
|
-
|
85
76
|
{
|
86
77
|
index: {
|
87
78
|
_index: index,
|
88
79
|
_id: record.id,
|
89
|
-
data: config.index_data(record)
|
90
|
-
|
80
|
+
data: config.index_data(record),
|
81
|
+
version: version,
|
82
|
+
version_type: :external
|
83
|
+
}
|
91
84
|
}
|
92
85
|
end
|
93
86
|
|
94
|
-
def execute_bulk(client: nil, index_name: nil)
|
87
|
+
def execute_bulk(client: nil, index_name: nil, refresh: false, &block)
|
95
88
|
clients = Array(client || config.clients)
|
96
89
|
|
97
90
|
clients.map do |current_client|
|
98
91
|
indices = Array(index_name || write_indices(current_client))
|
99
92
|
|
100
|
-
commands = indices.flat_map
|
93
|
+
commands = indices.flat_map(&block)
|
101
94
|
|
102
|
-
current_client.bulk(body: commands).tap do |result|
|
95
|
+
current_client.bulk(body: commands, refresh: refresh).tap do |result|
|
103
96
|
check_errors!(result)
|
104
97
|
end
|
105
98
|
end
|
@@ -109,11 +102,10 @@ module Zelastic
|
|
109
102
|
return false unless result['errors']
|
110
103
|
|
111
104
|
errors = result['items']
|
112
|
-
|
113
|
-
.compact
|
105
|
+
.filter_map { |item| item['error'] || item.fetch('index', {})['error'] }
|
114
106
|
|
115
107
|
ignorable_errors, important_errors = errors
|
116
|
-
|
108
|
+
.partition { |error| ignorable_error?(error) }
|
117
109
|
|
118
110
|
logger.warn("Ignoring #{ignorable_errors.count} version conflicts") if ignorable_errors.any?
|
119
111
|
|
@@ -123,16 +115,11 @@ module Zelastic
|
|
123
115
|
end
|
124
116
|
|
125
117
|
def ignorable_error?(error)
|
126
|
-
# rubocop:disable Metrics/LineLength
|
127
|
-
regexp =
|
128
|
-
if config.type?
|
129
|
-
/^\[#{config.type}\]\[\d+\]: version conflict, current version \[\d+\] is higher or equal to the one provided \[\d+\]$/
|
130
|
-
else
|
131
|
-
/^\[\d+\]: version conflict, current version \[\d+\] is higher or equal to the one provided \[\d+\]$/
|
132
|
-
end
|
133
|
-
# rubocop:enable Metrics/LineLength
|
134
118
|
error['type'] == 'version_conflict_engine_exception' &&
|
135
|
-
error['reason'] =~
|
119
|
+
error['reason'] =~ VERSION_CONFLICT_ERROR_REGEXP
|
136
120
|
end
|
121
|
+
|
122
|
+
VERSION_CONFLICT_ERROR_REGEXP =
|
123
|
+
/^\[\d+\]: version conflict, current version \[\d+\] is higher or equal to the one provided \[\d+\]$/.freeze
|
137
124
|
end
|
138
125
|
end
|
data/lib/zelastic/version.rb
CHANGED
data/log/.keep
ADDED
File without changes
|
data/zelastic.gemspec
CHANGED
@@ -22,13 +22,14 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
23
|
spec.require_paths = ['lib']
|
24
24
|
|
25
|
+
spec.add_dependency 'elasticsearch', '>= 7', '< 9'
|
26
|
+
|
27
|
+
spec.add_dependency 'activerecord'
|
25
28
|
spec.add_dependency 'activesupport'
|
26
29
|
|
27
|
-
spec.add_development_dependency '
|
28
|
-
spec.add_development_dependency '
|
29
|
-
spec.add_development_dependency '
|
30
|
-
spec.add_development_dependency '
|
31
|
-
spec.add_development_dependency '
|
32
|
-
spec.add_development_dependency 'rspec'
|
33
|
-
spec.add_development_dependency 'rubocop'
|
30
|
+
spec.add_development_dependency 'bundler', '~> 2'
|
31
|
+
spec.add_development_dependency 'carwow_rubocop', '~> 4'
|
32
|
+
spec.add_development_dependency 'pry', '~> 0.14'
|
33
|
+
spec.add_development_dependency 'rake', '~> 13'
|
34
|
+
spec.add_development_dependency 'rspec', '~> 3'
|
34
35
|
end
|
metadata
CHANGED
@@ -1,29 +1,35 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zelastic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- carwow Developers
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-08-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: elasticsearch
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '7'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '9'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
29
|
+
version: '7'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '9'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: activerecord
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -31,7 +37,7 @@ dependencies:
|
|
31
37
|
- - ">="
|
32
38
|
- !ruby/object:Gem::Version
|
33
39
|
version: '0'
|
34
|
-
type: :
|
40
|
+
type: :runtime
|
35
41
|
prerelease: false
|
36
42
|
version_requirements: !ruby/object:Gem::Requirement
|
37
43
|
requirements:
|
@@ -39,13 +45,13 @@ dependencies:
|
|
39
45
|
- !ruby/object:Gem::Version
|
40
46
|
version: '0'
|
41
47
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
48
|
+
name: activesupport
|
43
49
|
requirement: !ruby/object:Gem::Requirement
|
44
50
|
requirements:
|
45
51
|
- - ">="
|
46
52
|
- !ruby/object:Gem::Version
|
47
53
|
version: '0'
|
48
|
-
type: :
|
54
|
+
type: :runtime
|
49
55
|
prerelease: false
|
50
56
|
version_requirements: !ruby/object:Gem::Requirement
|
51
57
|
requirements:
|
@@ -53,75 +59,75 @@ dependencies:
|
|
53
59
|
- !ruby/object:Gem::Version
|
54
60
|
version: '0'
|
55
61
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
62
|
+
name: bundler
|
57
63
|
requirement: !ruby/object:Gem::Requirement
|
58
64
|
requirements:
|
59
|
-
- - "
|
65
|
+
- - "~>"
|
60
66
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
67
|
+
version: '2'
|
62
68
|
type: :development
|
63
69
|
prerelease: false
|
64
70
|
version_requirements: !ruby/object:Gem::Requirement
|
65
71
|
requirements:
|
66
|
-
- - "
|
72
|
+
- - "~>"
|
67
73
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
74
|
+
version: '2'
|
69
75
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
76
|
+
name: carwow_rubocop
|
71
77
|
requirement: !ruby/object:Gem::Requirement
|
72
78
|
requirements:
|
73
|
-
- - "
|
79
|
+
- - "~>"
|
74
80
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
81
|
+
version: '4'
|
76
82
|
type: :development
|
77
83
|
prerelease: false
|
78
84
|
version_requirements: !ruby/object:Gem::Requirement
|
79
85
|
requirements:
|
80
|
-
- - "
|
86
|
+
- - "~>"
|
81
87
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
88
|
+
version: '4'
|
83
89
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
90
|
+
name: pry
|
85
91
|
requirement: !ruby/object:Gem::Requirement
|
86
92
|
requirements:
|
87
|
-
- - "
|
93
|
+
- - "~>"
|
88
94
|
- !ruby/object:Gem::Version
|
89
|
-
version: '0'
|
95
|
+
version: '0.14'
|
90
96
|
type: :development
|
91
97
|
prerelease: false
|
92
98
|
version_requirements: !ruby/object:Gem::Requirement
|
93
99
|
requirements:
|
94
|
-
- - "
|
100
|
+
- - "~>"
|
95
101
|
- !ruby/object:Gem::Version
|
96
|
-
version: '0'
|
102
|
+
version: '0.14'
|
97
103
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
104
|
+
name: rake
|
99
105
|
requirement: !ruby/object:Gem::Requirement
|
100
106
|
requirements:
|
101
|
-
- - "
|
107
|
+
- - "~>"
|
102
108
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
109
|
+
version: '13'
|
104
110
|
type: :development
|
105
111
|
prerelease: false
|
106
112
|
version_requirements: !ruby/object:Gem::Requirement
|
107
113
|
requirements:
|
108
|
-
- - "
|
114
|
+
- - "~>"
|
109
115
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
116
|
+
version: '13'
|
111
117
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
118
|
+
name: rspec
|
113
119
|
requirement: !ruby/object:Gem::Requirement
|
114
120
|
requirements:
|
115
|
-
- - "
|
121
|
+
- - "~>"
|
116
122
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
123
|
+
version: '3'
|
118
124
|
type: :development
|
119
125
|
prerelease: false
|
120
126
|
version_requirements: !ruby/object:Gem::Requirement
|
121
127
|
requirements:
|
122
|
-
- - "
|
128
|
+
- - "~>"
|
123
129
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
130
|
+
version: '3'
|
125
131
|
description: An index manager for Elasticsearch and ActiveRecord
|
126
132
|
email:
|
127
133
|
- developers@carwow.co.uk
|
@@ -130,29 +136,31 @@ extensions: []
|
|
130
136
|
extra_rdoc_files: []
|
131
137
|
files:
|
132
138
|
- ".circleci/config.yml"
|
139
|
+
- ".github/dependabot.yml"
|
133
140
|
- ".gitignore"
|
134
141
|
- ".rspec"
|
135
142
|
- ".rubocop.yml"
|
136
|
-
- ".rubocop_todo.yml"
|
137
143
|
- CHANGELOG.md
|
138
144
|
- Gemfile
|
139
|
-
- Gemfile.lock
|
140
145
|
- LICENSE.txt
|
141
146
|
- README.md
|
142
147
|
- Rakefile
|
143
148
|
- bin/console
|
149
|
+
- bin/rspec
|
150
|
+
- bin/rubocop
|
144
151
|
- bin/setup
|
145
152
|
- lib/zelastic.rb
|
146
153
|
- lib/zelastic/config.rb
|
147
154
|
- lib/zelastic/index_manager.rb
|
148
155
|
- lib/zelastic/indexer.rb
|
149
156
|
- lib/zelastic/version.rb
|
157
|
+
- log/.keep
|
150
158
|
- zelastic.gemspec
|
151
159
|
homepage: https://github.com/carwow/zelastic
|
152
160
|
licenses:
|
153
161
|
- MIT
|
154
162
|
metadata: {}
|
155
|
-
post_install_message:
|
163
|
+
post_install_message:
|
156
164
|
rdoc_options: []
|
157
165
|
require_paths:
|
158
166
|
- lib
|
@@ -167,8 +175,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
175
|
- !ruby/object:Gem::Version
|
168
176
|
version: '0'
|
169
177
|
requirements: []
|
170
|
-
rubygems_version: 3.
|
171
|
-
signing_key:
|
178
|
+
rubygems_version: 3.3.11
|
179
|
+
signing_key:
|
172
180
|
specification_version: 4
|
173
181
|
summary: Zero-downtime (re-)indexing of ActiveRecord models into Elasticsearch.
|
174
182
|
test_files: []
|
data/.rubocop_todo.yml
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# This configuration was generated by
|
2
|
-
# `rubocop --auto-gen-config`
|
3
|
-
# on 2018-05-27 17:11:31 +0100 using RuboCop version 0.52.1.
|
4
|
-
# The point is for the user to remove these configuration records
|
5
|
-
# one by one as the offenses are removed from the code base.
|
6
|
-
# Note that changes in the inspected code, or installation of new
|
7
|
-
# versions of RuboCop, may require this file to be generated again.
|
8
|
-
|
9
|
-
# Offense count: 1
|
10
|
-
Lint/ShadowingOuterLocalVariable:
|
11
|
-
Exclude:
|
12
|
-
- 'lib/zelastic/indexer.rb'
|
13
|
-
|
14
|
-
# Offense count: 1
|
15
|
-
Metrics/AbcSize:
|
16
|
-
Max: 17
|
17
|
-
|
18
|
-
# Offense count: 1
|
19
|
-
# Configuration parameters: CountComments, ExcludedMethods.
|
20
|
-
Metrics/BlockLength:
|
21
|
-
Max: 83
|
22
|
-
|
23
|
-
# Offense count: 4
|
24
|
-
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
|
25
|
-
# URISchemes: http, https
|
26
|
-
Metrics/LineLength:
|
27
|
-
Max: 146
|
28
|
-
|
29
|
-
# Offense count: 1
|
30
|
-
Naming/AccessorMethodName:
|
31
|
-
Exclude:
|
32
|
-
- 'spec/zelastic/indexer_spec.rb'
|
data/Gemfile.lock
DELETED
@@ -1,91 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
zelastic (0.6.0)
|
5
|
-
activesupport
|
6
|
-
|
7
|
-
GEM
|
8
|
-
remote: https://rubygems.org/
|
9
|
-
specs:
|
10
|
-
activemodel (5.1.4)
|
11
|
-
activesupport (= 5.1.4)
|
12
|
-
activerecord (5.1.4)
|
13
|
-
activemodel (= 5.1.4)
|
14
|
-
activesupport (= 5.1.4)
|
15
|
-
arel (~> 8.0)
|
16
|
-
activesupport (5.1.4)
|
17
|
-
concurrent-ruby (~> 1.0, >= 1.0.2)
|
18
|
-
i18n (~> 0.7)
|
19
|
-
minitest (~> 5.1)
|
20
|
-
tzinfo (~> 1.1)
|
21
|
-
arel (8.0.0)
|
22
|
-
ast (2.3.0)
|
23
|
-
coderay (1.1.2)
|
24
|
-
concurrent-ruby (1.0.5)
|
25
|
-
diff-lcs (1.3)
|
26
|
-
elasticsearch (6.0.2)
|
27
|
-
elasticsearch-api (= 6.0.2)
|
28
|
-
elasticsearch-transport (= 6.0.2)
|
29
|
-
elasticsearch-api (6.0.2)
|
30
|
-
multi_json
|
31
|
-
elasticsearch-transport (6.0.2)
|
32
|
-
faraday
|
33
|
-
multi_json
|
34
|
-
faraday (0.15.2)
|
35
|
-
multipart-post (>= 1.2, < 3)
|
36
|
-
i18n (0.9.1)
|
37
|
-
concurrent-ruby (~> 1.0)
|
38
|
-
method_source (0.9.0)
|
39
|
-
minitest (5.11.1)
|
40
|
-
multi_json (1.13.1)
|
41
|
-
multipart-post (2.0.0)
|
42
|
-
parallel (1.12.1)
|
43
|
-
parser (2.4.0.2)
|
44
|
-
ast (~> 2.3)
|
45
|
-
powerpack (0.1.1)
|
46
|
-
pry (0.11.3)
|
47
|
-
coderay (~> 1.1.0)
|
48
|
-
method_source (~> 0.9.0)
|
49
|
-
rainbow (3.0.0)
|
50
|
-
rake (13.0.1)
|
51
|
-
rspec (3.7.0)
|
52
|
-
rspec-core (~> 3.7.0)
|
53
|
-
rspec-expectations (~> 3.7.0)
|
54
|
-
rspec-mocks (~> 3.7.0)
|
55
|
-
rspec-core (3.7.1)
|
56
|
-
rspec-support (~> 3.7.0)
|
57
|
-
rspec-expectations (3.7.0)
|
58
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
59
|
-
rspec-support (~> 3.7.0)
|
60
|
-
rspec-mocks (3.7.0)
|
61
|
-
diff-lcs (>= 1.2.0, < 2.0)
|
62
|
-
rspec-support (~> 3.7.0)
|
63
|
-
rspec-support (3.7.0)
|
64
|
-
rubocop (0.52.1)
|
65
|
-
parallel (~> 1.10)
|
66
|
-
parser (>= 2.4.0.2, < 3.0)
|
67
|
-
powerpack (~> 0.1)
|
68
|
-
rainbow (>= 2.2.2, < 4.0)
|
69
|
-
ruby-progressbar (~> 1.7)
|
70
|
-
unicode-display_width (~> 1.0, >= 1.0.1)
|
71
|
-
ruby-progressbar (1.9.0)
|
72
|
-
thread_safe (0.3.6)
|
73
|
-
tzinfo (1.2.4)
|
74
|
-
thread_safe (~> 0.1)
|
75
|
-
unicode-display_width (1.3.0)
|
76
|
-
|
77
|
-
PLATFORMS
|
78
|
-
ruby
|
79
|
-
|
80
|
-
DEPENDENCIES
|
81
|
-
activerecord
|
82
|
-
bundler
|
83
|
-
elasticsearch
|
84
|
-
pry
|
85
|
-
rake
|
86
|
-
rspec
|
87
|
-
rubocop
|
88
|
-
zelastic!
|
89
|
-
|
90
|
-
BUNDLED WITH
|
91
|
-
1.17.2
|