diff --git a/.buildkite/pipeline.yml b/.buildkite/pipeline.yml index ce785fff29..7458881c7d 100644 --- a/.buildkite/pipeline.yml +++ b/.buildkite/pipeline.yml @@ -1,29 +1,4 @@ steps: - - label: ":elasticsearch: Elasticsearch :ruby: {{ matrix.ruby_source}}:{{ matrix.ruby }} :phone: Transport {{ matrix.transport }} - {{ matrix.suite }}" - agents: - provider: "gcp" - env: - RUBY_VERSION: "{{ matrix.ruby }}" - TEST_SUITE: "{{ matrix.suite }}" - STACK_VERSION: 9.0.0-SNAPSHOT - TRANSPORT_VERSION: "{{ matrix.transport }}" - RUBY_SOURCE: "{{ matrix.ruby_source }}" - QUIET: false - matrix: - setup: - suite: - - "free" - - "platinum" - ruby: - - "3.3" - transport: - - "8.3" - ruby_source: - - "ruby" - command: ./.buildkite/run-tests.sh - # I'm publishing test results to HTML and JUnit in this directory and this directive makes them - # available in the Artifacts tab of a build in Buildkite. - artifact_paths: "elasticsearch-api/tmp/*" - label: ":yaml: YAML test suite :ruby: {{ matrix.ruby_source}}:{{ matrix.ruby }} :phone: Transport {{ matrix.transport }}" agents: provider: "gcp" @@ -38,10 +13,19 @@ steps: transport: - "8.3" adjustments: - - with: + - with: # JRuby tests ruby: "9.4" ruby_source: "jruby" transport: "8.3" + # Test for different versions of transport + - with: + ruby: "3.3" + ruby_source: "ruby" + transport: "main" + - with: + ruby: "3.3" + ruby_source: "ruby" + transport: "8.2" env: RUBY_VERSION: "{{ matrix.ruby }}" STACK_VERSION: 9.0.0-SNAPSHOT diff --git a/CHANGELOG-9.x.md b/CHANGELOG-9.x.md new file mode 100644 index 0000000000..27708b0f73 --- /dev/null +++ b/CHANGELOG-9.x.md @@ -0,0 +1,14 @@ +# CHANGELOG 9.x + +## Elasticsearch API + +### Development + +**Integration Tests** + +Migrated away from the Elasticsearch REST API tests and test runner in CI. We now run the [Elasticsearch Client tests](https://github.com/elastic/elasticsearch-clients-tests/) with the [Elasticsearch Tests Runner](https://github.com/elastic/es-test-runner-ruby/**. This gives us more control on what we're testing and makes the Buildkite build way faster in Pull Requests and scheduled builds. + + +**Rake tasks** + +Some old rake tasks that were not being used have been removed. The rest were streamlined, the `es` namespace has been streamlined to make it easier to run Elasticsearch with Docker during development. The `docker` namespace was merged into `es`. diff --git a/Rakefile b/Rakefile index 062a1f62c4..3d1cbacdea 100644 --- a/Rakefile +++ b/Rakefile @@ -51,8 +51,6 @@ import 'rake_tasks/elasticsearch_tasks.rake' import 'rake_tasks/test_tasks.rake' import 'rake_tasks/doc_generator.rake' import 'rake_tasks/docker_tasks.rake' -import 'rake_tasks/update_version.rake' -import 'profile/benchmarking/benchmarking_tasks.rake' require 'pathname' CURRENT_PATH = Pathname(File.expand_path(__dir__)) diff --git a/benchmarks/Dockerfile b/benchmarks/Dockerfile deleted file mode 100644 index fb148f89d2..0000000000 --- a/benchmarks/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -# $ docker build --file benchmarks/Dockerfile --tag elastic/elasticsearch-ruby . - -ARG RUBY_VERSION=2.7 -FROM ruby:${RUBY_VERSION} - -ENV TERM xterm-256color -ENV GEM_HOME="/usr/local/bundle" -ENV PATH $GEM_HOME/bin:$GEM_HOME/gems/bin:$PATH - -VOLUME ["/data"] - -WORKDIR /elasticsearch-ruby - -# TODO(karmi): Copy dependencies first to make use of Docker layer caching -COPY . . - -RUN bundle install --retry=5 -RUN cd benchmarks && bundle install - -CMD ["/bin/bash"] diff --git a/benchmarks/Gemfile b/benchmarks/Gemfile deleted file mode 100644 index 45b087784b..0000000000 --- a/benchmarks/Gemfile +++ /dev/null @@ -1,32 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# frozen_string_literal: true - -source "https://rubygems.org" - -git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } - -gem 'elasticsearch', path: File.expand_path('../../elasticsearch', __FILE__), require: false -gem 'elasticsearch-api', path: File.expand_path('../../elasticsearch-api', __FILE__), require: false -gem 'elasticsearch-transport', path: File.expand_path('../../elasticsearch-transport', __FILE__), require: false - -gem 'ansi' -gem 'patron' -gem 'oj' - -gem 'pry', group: :development diff --git a/benchmarks/actions/001_ping.rb b/benchmarks/actions/001_ping.rb deleted file mode 100644 index d7ea49a855..0000000000 --- a/benchmarks/actions/001_ping.rb +++ /dev/null @@ -1,27 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require_relative '../lib/benchmarks' - -Benchmarks.register \ - action: 'ping', - category: 'core', - warmups: Benchmarks::DEFAULT_WARMUPS, - repetitions: 10_000, - measure: Proc.new { |n, runner| - runner.runner_client.ping - } diff --git a/benchmarks/actions/002_info.rb b/benchmarks/actions/002_info.rb deleted file mode 100644 index 1b095ab02e..0000000000 --- a/benchmarks/actions/002_info.rb +++ /dev/null @@ -1,26 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -require_relative '../lib/benchmarks' - -Benchmarks.register \ - action: 'info', - category: 'core', - warmups: Benchmarks::DEFAULT_WARMUPS, - repetitions: 10_000, - measure: Proc.new { |n, runner| - runner.runner_client.info - } diff --git a/benchmarks/actions/003_get.rb b/benchmarks/actions/003_get.rb deleted file mode 100644 index dd612e77ed..0000000000 --- a/benchmarks/actions/003_get.rb +++ /dev/null @@ -1,34 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require_relative '../lib/benchmarks' - -Benchmarks.register \ - action: 'get', - category: 'core', - warmups: 100, - repetitions: 10_000, - setup: Proc.new { |runner| - runner.runner_client.indices.delete(index: 'test-bench-get', ignore: 404) - runner.runner_client.index index: 'test-bench-get', id: '1', body: { title: 'Test' } - runner.runner_client.cluster.health(wait_for_status: 'yellow') - runner.runner_client.indices.refresh index: 'test-bench-get' - }, - measure: Proc.new { |n, runner| - response = runner.runner_client.get index: 'test-bench-get', id: '1' - raise RuntimeError.new("Incorrect data: #{response}") unless response["_source"]["title"] == "Test" - } diff --git a/benchmarks/actions/004_index.rb b/benchmarks/actions/004_index.rb deleted file mode 100644 index 5428228787..0000000000 --- a/benchmarks/actions/004_index.rb +++ /dev/null @@ -1,35 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require_relative '../lib/benchmarks' - -Benchmarks.register \ - action: 'index', - category: 'core', - warmups: 100, - repetitions: 10_000, - setup: Proc.new { |runner| - runner.runner_client.indices.delete(index: 'test-bench-index', ignore: 404) - runner.runner_client.indices.create(index: 'test-bench-index') - runner.runner_client.cluster.health(wait_for_status: 'yellow') - }, - measure: Proc.new { |n, runner| - doc_path = Benchmarks.data_path.join('small/document.json') - raise RuntimeError.new("Document at #{doc_path} not found") unless doc_path.exist? - response = runner.runner_client.index index: 'test-bench-index', id: "%04d-%04d" % [n, rand(1..1000)], body: doc_path.open.read - raise RuntimeError.new("Incorrect response: #{response}") unless response["result"] == "created" - } diff --git a/benchmarks/actions/005_bulk.rb b/benchmarks/actions/005_bulk.rb deleted file mode 100644 index 03213e7a1a..0000000000 --- a/benchmarks/actions/005_bulk.rb +++ /dev/null @@ -1,46 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require_relative '../lib/benchmarks' - -Benchmarks.register \ - action: 'bulk', - category: 'core', - warmups: 10, - repetitions: 1_000, - operations: 10_000, - setup: Proc.new { |runner| - runner.runner_client.indices.delete(index: 'test-bench-bulk', ignore: 404) - runner.runner_client.indices.create(index: 'test-bench-bulk', body: '{"settings": { "number_of_shards": 3, "refresh_interval":"5s"}}') - runner.runner_client.cluster.health(wait_for_status: 'yellow') - }, - measure: Proc.new { |n, runner| - doc_path = Benchmarks.data_path.join('small/document.json') - raise RuntimeError.new("Document at #{doc_path} not found") unless doc_path.exist? - doc_body = doc_path.open.read.tr("\n", "").gsub(/\s{2,}/, "") + "\n" - - op_meta = %Q|{"index":{}}\n|.freeze - op_body = '' - - 1.upto(runner.operations).each do |i| - op_body << op_meta - op_body << doc_body - end - - response = runner.runner_client.bulk index: 'test-bench-bulk', body: op_body - raise RuntimeError.new("Incorrect response: #{response}") if response["errors"] - } diff --git a/benchmarks/bin/run.rb b/benchmarks/bin/run.rb deleted file mode 100755 index 52866fb139..0000000000 --- a/benchmarks/bin/run.rb +++ /dev/null @@ -1,139 +0,0 @@ -#!/usr/bin/env ruby -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -require 'ansi/core' -require 'logger' -require 'patron' -require 'pathname' -require 'oj' - -require_relative '../lib/benchmarks' - -puts "Running benchmarks for elasticsearch-ruby@#{Elasticsearch::VERSION}".ansi(:bold,:underline) - -config = { - "ELASTICSEARCH_TARGET_URL" => "", - "ELASTICSEARCH_REPORT_URL" => "", - "DATA_SOURCE" => "", - "BUILD_ID" => "", - "TARGET_SERVICE_TYPE" => "", - "TARGET_SERVICE_NAME" => "", - "TARGET_SERVICE_VERSION" => "", - "TARGET_SERVICE_OS_FAMILY" => "", - "CLIENT_BRANCH" => "", - "CLIENT_COMMIT" => "", - "CLIENT_BENCHMARK_ENVIRONMENT" => "" -} - -missing_keys = [] - -config.keys.each do |key| - if ENV[key] && !ENV[key].to_s.empty? - config[key] = ENV[key] - else - missing_keys << key - end -end - -unless missing_keys.empty? -puts "ERROR: Required environment variables [#{missing_keys.join(',')}] missing".ansi(:bold, :red) - exit(1) -end - -start = Time.now.utc - -runner_client = Elasticsearch::Client.new(url: config["ELASTICSEARCH_TARGET_URL"]) -report_client = Elasticsearch::Client.new( - url: config["ELASTICSEARCH_REPORT_URL"], - request_timeout: 5*60, - retry_on_failure: 10 -) -if ENV['DEBUG'] - logger = Logger.new(STDOUT) - logger.level = Logger::INFO - logger.formatter = proc { |s, d, p, m| "#{m}\n".ansi(:faint) } - - runner_client.transport.logger = logger - report_client.transport.logger = logger -end - -runner = Benchmarks::Runner::Runner.new \ - build_id: config['BUILD_ID'], - environment: config['CLIENT_BENCHMARK_ENVIRONMENT'], - category: ENV['CLIENT_BENCHMARK_CATEGORY'].to_s, - runner_client: runner_client, - report_client: report_client, - target: { - service: { - type: config['TARGET_SERVICE_TYPE'], - name: config['TARGET_SERVICE_NAME'], - version: config['TARGET_SERVICE_VERSION'], - git: { - branch: ENV['TARGET_SERVICE_GIT_BRANCH'], - commit: ENV['TARGET_SERVICE_GIT_COMMIT'] - } - }, - os: { - family: config['TARGET_SERVICE_OS_FAMILY'] - } - }, - runner: { - service: { - git: { - branch: config['CLIENT_BRANCH'], - commit: config['CLIENT_COMMIT'] - } - } - } - -# ----- Run benchmarks -------------------------------------------------------- - -Benchmarks.data_path = Pathname(config["DATA_SOURCE"]) -unless Benchmarks.data_path.exist? - puts "ERROR: Data source at [#{Benchmarks.data_path}] not found".ansi(:bold, :red) - exit(1) -end - -Dir[File.expand_path("../../actions/*.rb", __FILE__)].each { |file| require file } - -Benchmarks.actions.each do |b| - next unless ENV['FILTER'].nil? or ENV['FILTER'].include? b.action - - runner.setup(&b.setup) if b.setup - - result = runner.measure( - action: b.action, - category: b.category, - warmups: b.warmups, - repetitions: b.repetitions, - operations: b.operations, - &b.measure).run! - - puts " " + - "[#{b.action}] ".ljust(16) + - "#{b.repetitions}x ".ljust(10) + - "mean=".ansi(:faint) + - "#{coll = runner.stats.map(&:duration); ((coll.sum / coll.size.to_f)/1e+6).round}ms " + - "runner=".ansi(:faint)+ - "#{runner.stats.any? { |s| s.outcome == 'failure' } ? 'failure' : 'success' } ".ansi( runner.stats.none? { |s| s.outcome == 'failure' } ? :green : :red ) + - "report=".ansi(:faint)+ - "#{result ? 'success' : 'failure' }".ansi( result ? :green : :red ) -end - -# ----------------------------------------------------------------------------- - -puts "Finished in #{(Time.mktime(0)+(Time.now.utc-start)).strftime("%H:%M:%S")}".ansi(:underline) diff --git a/benchmarks/lib/benchmarks.rb b/benchmarks/lib/benchmarks.rb deleted file mode 100644 index 09a621e471..0000000000 --- a/benchmarks/lib/benchmarks.rb +++ /dev/null @@ -1,339 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require "ostruct" -require "time" -require "rbconfig" - -require "ansi/core" -require "elasticsearch" - -## -# Module Benchmarks contains components for end-to-end benchmarking of the Ruby client for Elasticsearch. -# -module Benchmarks - DEFAULT_WARMUPS = 0 - DEFAULT_REPETITIONS = 1_000 - DEFAULT_OPERATIONS = 1 - - ## - # Represents the benchmarking action. - # - class Action - attr_reader :action, :category, :warmups, :repetitions, :operations, :setup, :measure - - # @param action [String] The name of the measured action - # @param category [String] The category of the measured action - # @param warmups [Number] The number of warmup runs - # @param repetitions [Number] The number of repetitions - # @param operations [Number] The number of operations in a single repetition - # @param setup [Block] The operation setup - # @param measure [Block] The measured operation - def initialize(action:, category:, warmups:, repetitions:, operations:, setup:, measure:) - raise ArgumentError.new("Required parameter [action] empty") if action.empty? - raise ArgumentError.new("Required parameter [category] empty") if category.empty? - - @action = action - @category = category - @warmups = warmups || DEFAULT_WARMUPS - @repetitions = repetitions || DEFAULT_REPETITIONS - @operations = operations || DEFAULT_OPERATIONS - @setup = setup - @measure = measure - end - end - - ## - # Registers an action for benchmarking. - # - # @option arguments [String] :action The name of the measured action - # @option arguments [String] :category The category of the measured action - # @option arguments [Number] :warmups The number of warmup runs - # @option arguments [Number] :repetitions The number of repetitions - # @option arguments [Number] :operations The number of operations in a single repetition - # @option arguments [Block] :setup The operation setup - # @option arguments [Block] :measure The measured operation - # - def self.register(arguments = {}) - self.actions << Action.new( - action: arguments[:action], - category: arguments[:category], - warmups: arguments[:warmups], - repetitions: arguments[:repetitions], - operations: arguments[:operations], - setup: arguments[:setup], - measure: arguments[:measure] - ) - end - - ## - # Set data path for benchmarks. - # - # @param path [Pathname,String] - # - def self.data_path=(path) - @data_path = Pathname(path) - end - - ## - # Return data path for benchmarks. - # - # @return [Pathname] - # - def self.data_path - @data_path - end - - ## - # Returns the registered actions. - # - # @return [Array] - # - def self.actions - @actions ||= [] - end - - ## - # Module Runner contains components for running the benchmarks. - # - module Runner - ## - # Stats represents the measured statistics. - # - class Stats < OpenStruct; end - - ## - # Errors contain error class for runner operations. - # - module Errors - ## - # ReportError represents an exception ocurring during reporting the results. - # - class ReportError < StandardError; end - - ## - # SetupError represents an exception occuring during operation setup. - # - class SetupError < StandardError; end - - ## - # WarmupError represents an exception occuring during operation warmup. - # - class WarmupError < StandardError; end - end - - ## - # The bulk size for reporting results. - # - BULK_BATCH_SIZE = 1000 - - ## - # The index name for reporting results. - # - INDEX_NAME="metrics-intake-#{Time.now.strftime("%Y-%m")}" - - ## - # Runner represents a benchmarking runner. - # - # It is initialized with two Elasticsearch clients, one for running the benchmarks, - # another one for reporting the results. - # - # Use the {#measure} method for adding a block which is executed and measured. - # - class Runner - attr_reader :stats, :runner_client, :report_client, :warmups, :repetitions, :operations - - ## - # @param runner_client [Elasticsearch::Client] The client for executing the measured operations. - # @param report_client [Elasticsearch::Client] The client for storing the results. - # - def initialize(build_id:, category:, environment:, runner_client:, report_client:, target:, runner:) - raise ArgumentError.new("Required parameter [build_id] empty") if build_id.empty? - raise ArgumentError.new("Required parameter [environment] empty") if environment.empty? - - @action = '' - @stats = [] - @warmups = 0 - @repetitions = 0 - @operations = 0 - - @build_id = build_id - @category = category - @environment = environment - @runner_client = runner_client - @report_client = report_client - @target_config = target - @runner_config = runner - end - - ## - # Executes the measured block, capturing statistics, and reports the results. - # - # @return [Boolean] - # @raise [Errors::ReportError] - # - def run! - @stats = [] - - # Run setup code - begin - @setup.arity < 1 ? self.instance_eval(&@setup) : @setup.call(self) if @setup - rescue StandardError => e - raise Errors::SetupError.new(e.inspect) - end - - # Run warmups - begin - @warmups.times do |n| - @measure.arity < 1 ? self.instance_eval(&@measure) : @measure.call(n, self) if @measure - end - rescue StandardError => e - raise Errors::WarmupError.new(e.inspect) - end - - # Run measured repetitions - # - # Cf. https://blog.dnsimple.com/2018/03/elapsed-time-with-ruby-the-right-way/ - @repetitions.times do |n| - stat = Stats.new(start: Time.now.utc) - start = Process.clock_gettime(Process::CLOCK_MONOTONIC) - begin - result = @measure.arity < 1 ? result = self.instance_eval(&@measure) : result = @measure.call(n, self) if @measure - if result == false - stat.outcome = "failure" - else - stat.outcome = "success" - end - rescue StandardError => e - stat.outcome = "failure" - ensure - stat.duration = ((Process.clock_gettime(Process::CLOCK_MONOTONIC)-start) * 1e+9 ).to_i - @stats << stat - end - end - - # Report results - begin - __report - rescue StandardError => e - puts "ERROR: #{e.inspect}" - return false - end - - return true - end - - ## - # Configure a setup for the measure operation. - # - # @return [self] - # - def setup &block - @setup = block - return self - end - - ## - # Configure the measure operation. - # - # @param action [String] A human-readable name of the operation. - # @param category [String] The operation category. - # @param warmups [Number] The number of warmups. - # @param repetitions [Number] The number of repetitions. - # @param operations [Number] The number of operations in a single repetition. - # - # @return [self] - # - def measure(action:, category:, warmups:, repetitions:, operations:, &block) - raise ArgumentError.new("Required parameter [action] empty") if action.empty? - raise ArgumentError.new("Required parameter [category] empty") if category.empty? - raise ArgumentError.new("Required parameter [repetitions] not a number") unless repetitions.is_a? Numeric - raise ArgumentError.new("Required parameter [operations] not a number") unless operations.is_a? Numeric - - @action = action - @category = category - @warmups = warmups - @repetitions = repetitions - @operations = operations - @measure = block - return self - end - - ## - # Stores the result in the reporting cluster. - # - # @api private - # - def __report - @stats.each_slice(BULK_BATCH_SIZE) do |slice| - payload = slice.map do |s| - { index: { - data: { - :'@timestamp' => s.start.iso8601, - labels: { - build_id: @build_id, - client: 'elasticsearch-ruby', - environment: @environment.to_s - }, - tags: ['bench', 'elasticsearch-ruby'], - event: { - action: @action, - duration: s.duration, - outcome: s.outcome - }, - benchmark: { - build_id: @build_id, - environment: @environment.to_s, - category: @category.to_s, - repetitions: @repetitions, - operations: @operations, - runner: { - service: @runner_config[:service].merge({ - type: 'client', - name: 'elasticsearch-ruby', - version: Elasticsearch::VERSION - }), - runtime: { - name: 'ruby', version: RbConfig::CONFIG['ruby_version'] - }, - os: { - family: RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase - } - }, - target: @target_config - } - } - } - } - end - - begin - - rescue Elasticsearch::Transport::Transport::Error => e - puts "ERROR: #{e.inspect}" - raise e - end - - response = @report_client.bulk index: INDEX_NAME, body: payload - if response['errors'] || response['items'].any? { |i| i.values.first['status'] > 201 } - raise Errors::ReportError.new("Error saving benchmark results to report cluster") - end - end - end - end - end -end diff --git a/elasticsearch-api/README.md b/elasticsearch-api/README.md index c87ec9ab5d..30d4489ed6 100644 --- a/elasticsearch-api/README.md +++ b/elasticsearch-api/README.md @@ -4,7 +4,7 @@ **Refer to the [official documentation on Elasticsearch API](https://www.elastic.co/guide/en/elasticsearch/client/ruby-api/current/api.html).** -The `elasticsearch-api` library provides a Ruby implementation of the [Elasticsearch](http://elasticsearch.com) REST API. It does not provide an Elasticsearch client; see the [`elastic-transport`](https://github.com/elastic/elastic-transport-ruby/) library. +The `elasticsearch-api` library provides a Ruby implementation of the [Elasticsearch](http://elasticsearch.com) REST API. It does not provide an Elasticsearch client. See [elasticsearch](https://github.com/elastic/elasticsearch-ruby) and the [`elastic-transport`](https://github.com/elastic/elastic-transport-ruby/) libraries for a full Elasticsearch client and HTTP transport layer respectively. We follow Ruby’s own maintenance policy and officially support all currently maintained versions per [Ruby Maintenance Branches](https://www.ruby-lang.org/en/downloads/branches/). @@ -15,17 +15,17 @@ Language clients are forward compatible; meaning that clients support communicat Refer to [CONTRIBUTING](https://github.com/elastic/elasticsearch-ruby/blob/main/CONTRIBUTING.md). -We run the test suite for Elasticsearch's Rest API tests. You can read more about this in [the test runner README](https://github.com/elastic/elasticsearch-ruby/tree/main/api-spec-testing#rest-api-yaml-test-runner). +The integration tests on this project run the [Elasticsearch Client tests](https://github.com/elastic/elasticsearch-clients-tests/) with the [Elasticsearch Tests Runner](https://github.com/elastic/es-test-runner-ruby/) library. This runs in CI against an Elasticsearch cluster in Docker. You can run a docker container with Elasticsearch with a Rake task from the root directory of this project: -The `rest_api` task needs the test files from Elasticsearch. You can run the rake task to download the test artifacts in the root folder of the project. You can pass in a version to the task as a parameter: +```bash +$ rake es:up +``` -`rake download_artifacts[8.5.0-SNAPSHOT]` +This will start whatever version of Elasticsearch is set in the Buildkite pipeline file (`../.buildkite/pipeline.yml`) with security enabled. You can also specify a version and a suite ('free' or 'platinum' for security disabled/enabled): -Or it can get the version from a running cluster to determine which version and build hash of Elasticsearch to use and test against: - -`TEST_ES_SERVER=http://localhost:9200 rake es:download_artifacts` - -This will download the necessary files used for the integration tests to `./tmp`. +```bash +$ rake es:start[version,suite] # e.g. rake es:start[9.0.0-SNAPSHOT, free] +``` ### Code generation diff --git a/elasticsearch-api/lib/elasticsearch/api.rb b/elasticsearch-api/lib/elasticsearch/api.rb index 02ec70f9ea..1a8471002e 100644 --- a/elasticsearch-api/lib/elasticsearch/api.rb +++ b/elasticsearch-api/lib/elasticsearch/api.rb @@ -27,6 +27,8 @@ Dir[ File.expand_path('../api/namespace/**/*.rb', __FILE__) ].each { |f| require f } module Elasticsearch + # This is the main module for including all API endpoint functions + # It includes the namespace modules from ./api/actions module API DEFAULT_SERIALIZER = MultiJson diff --git a/profile/benchmarking.rb b/profile/benchmarking.rb deleted file mode 100644 index 331259a509..0000000000 --- a/profile/benchmarking.rb +++ /dev/null @@ -1,85 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'benchmark' -require 'yaml' -require 'erb' -require 'json' -require_relative 'benchmarking/measurable' -require_relative 'benchmarking/simple' -require_relative 'benchmarking/complex' -require_relative 'benchmarking/results' - -module Elasticsearch - - # Module with all functionality for running client benchmark tests. - # - # @since 7.0.0 - module Benchmarking - - extend self - - # The default number of test repetitions. - # - # @return [ Integer ] The number of test repetitions. - # - # @since 7.0.0 - DEFAULT_TEST_REPETITIONS = 10.freeze - - # The number of default warmup repetitions of the test to do before - # recording times. - # - # @return [ Integer ] The default number of warmup repetitions. - # - # @since 7.0.0 - DEFAULT_WARMUP_REPETITIONS = 1.freeze - - # The default definition of a test run. - # - # @return [ Hash ] The default test run definition. - # - # @since 7.0.0 - DEFAULT_RUN = { 'description' => 'Default run', - 'repetitions' => { - 'measured' => DEFAULT_TEST_REPETITIONS, - 'warmup' => DEFAULT_WARMUP_REPETITIONS }, - 'name' => 'default', - 'metrics' => ['mean'] }.freeze - - # Parse a file of run definitions and yield each run. - # - # @params [ String ] file The YAML file containing the matrix of test run definitions. - # - # @yieldparam [ Hash ] A test run definition. - # - # @since 7.0.0 - def each_run(file) - if file - file = File.new(file) - matrix = YAML.load(ERB.new(file.read).result) - file.close - - matrix.each_with_index do |run, i| - DEFAULT_RUN.merge(run) - yield(run, i) - end - else - yield(DEFAULT_RUN) - end - end - end -end diff --git a/profile/benchmarking/benchmarking_tasks.rake b/profile/benchmarking/benchmarking_tasks.rake deleted file mode 100644 index aae5bef30e..0000000000 --- a/profile/benchmarking/benchmarking_tasks.rake +++ /dev/null @@ -1,201 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require_relative "../benchmarking" - -namespace :benchmark do - - namespace :simple do - - desc "Run the \'ping\' benchmark test" - task :ping do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} PING #{'*' * 5} \n" - puts "#{task.run(:ping)}" - end - end - - desc "Run the \'create index\' benchmark test" - task :create_index do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} CREATE INDEX #{'*' * 5} \n" - puts "#{task.run(:create_index)}" - end - end - - desc "Run the \'index smal document\' benchmark test with patron adapter" - task :index_document_small_patron do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - begin - require 'patron' - rescue LoadError - puts "Patron not loaded, skipping test" - else - task = Elasticsearch::Benchmarking::Simple.new(run, :patron) - puts "#{'*' * 5} INDEX SMALL DOCUMENT, PATRON #{'*' * 5} \n" - puts "#{task.run(:index_document_small)}" - end - end - end - - desc "Run the \'index small document\' benchmark test" - task :index_document_small do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} INDEX SMALL DOCUMENT #{'*' * 5} \n" - puts "#{task.run(:index_document_small)}" - end - end - - desc "Run the \'index large document\' benchmark test" - task :index_document_large do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} INDEX LARGE DOCUMENT #{'*' * 5} \n" - puts "#{task.run(:index_document_large)}" - end - end - - desc "Run the \'get small document\' benchmark test" - task :get_document_small do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} GET SMALL DOCUMENT #{'*' * 5} \n" - puts "#{task.run(:get_document_small)}" - end - end - - desc "Run the \'get large document\' benchmark test" - task :get_document_large do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} GET LARGE DOCUMENT #{'*' * 5} \n" - puts "#{task.run(:get_document_large)}" - end - end - - desc "Run the \'search small document\' benchmark test" - task :search_document_small do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} SEARCH SMALL DOCUMENT #{'*' * 5} \n" - puts "#{task.run(:search_document_small)}" - end - end - - desc "Run the \'search small document\' benchmark test" - task :search_document_large do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} SEARCH LARGE DOCUMENT #{'*' * 5} \n" - puts "#{task.run(:search_document_large)}" - end - end - - desc "Run the \'update document\' benchmark test" - task :update_document do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Simple.new(run) - puts "#{'*' * 5} UPDATE DOCUMENT #{'*' * 5} \n" - puts "#{task.run(:update_document)}" - end - end - - desc "Run all simple benchmark tests" - task :all, [:matrix] do |t, args| - %w[ benchmark:simple:ping - benchmark:simple:create_index - benchmark:simple:index_document_small - benchmark:simple:index_document_large - benchmark:simple:get_document_small - benchmark:simple:get_document_large - benchmark:simple:search_document_small - benchmark:simple:search_document_large - benchmark:simple:update_document - ].each do |task_name| - begin - require 'elasticsearch' - Rake::Task[task_name].invoke(*args) - rescue => ex - puts "Error in task [#{task_name}], #{ex.inspect}" - next - end - end - end - - # namespace :noop do - # - # desc "Run the \'search small document\' benchmark test with the noop plugin" - # task :search_document_small do - # puts "SIMPLE REQUEST BENCHMARK:: SEARCH SMALL DOCUMENT WITH NOOP PLUGIN" - # Elasticsearch::Benchmarking::Simple.new.run(:search_document_small, noop: true) - # end - # end - end - - namespace :complex do - - task :download_dataset do - current_path = File.expand_path(File.dirname(__FILE__)) - data_path = [current_path, 'data'].join('/') - unless File.exists?([data_path, 'stackoverflow.json'].join('/')) - `gsutil cp gs://clients-team-files/stackoverflow.json "#{data_path}/"` - end - end - - desc "Run the \'index documents\' benchmark test" - task :index_documents => :download_dataset do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Complex.new(run) - puts "#{'*' * 5} INDEX DOCUMENTS #{'*' * 5} \n" - puts "#{task.run(:index_documents)}" - end - end - - desc "Run the \'search documents\' benchmark test" - task :search_documents do - require 'elasticsearch' - Elasticsearch::Benchmarking.each_run(ENV['matrix']) do |run| - task = Elasticsearch::Benchmarking::Complex.new(run) - puts "#{'*' * 5} SEARCH DOCUMENTS #{'*' * 5} \n" - puts "#{task.run(:search_documents)}" - end - end - - desc "Run all complex benchmark test" - task :all do - %w[ benchmark:complex:index_documents - ].each do |task_name| - require 'elasticsearch' - Rake::Task[task_name].invoke - end - end - end -end diff --git a/profile/benchmarking/complex.rb b/profile/benchmarking/complex.rb deleted file mode 100644 index b910ddd5bf..0000000000 --- a/profile/benchmarking/complex.rb +++ /dev/null @@ -1,120 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Benchmarking - - # Class encapsulating all settings and functionality for running benchmarking - # tests making complex requests. - # - # @since 7.0.0 - class Complex - include Measurable - - # Test sending a bulk request to index a large dataset. - # - # @example Test sending a bulk index request. - # task.create_documents(opts) - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results documents. - # - # @since 7.0.0 - def index_documents(opts = {}) - results = [] - slices = dataset_slices - - warmup_repetitions.times do - slices.each do |slice| - client.bulk(body: slice) - end - end - - duration = with_cleanup do - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - slices.each do |slice| - client.bulk(body: slice) - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: File.basename(DATASET_FILE), - dataset_size: ObjectSpace.memsize_of(dataset), - dataset_n_documents: dataset.length } - index_results!(results, options) - end - - # Test sending a request a search request. - # - # @example Test sending a search request. - # Benchmarking::Complex.search_documents(10) - # - # @param [ Integer ] repetitions The number of test repetitions. - # - # @since 7.0.0 - def search_documents(opts = {}) - results = [] - - duration = with_cleanup do - slices = dataset_slices - sample_slice = slices.collect do |slice| - client.bulk(body: slice) - slice - end[rand(slices.size)-1] - - sample_document = sample_slice[rand(sample_slice.size)-1][:index][:data] - search_criteria = sample_document.find { |k,v| v.is_a?(String) } - request = { body: { query: { match: { search_criteria[0] => search_criteria[1] } } } } - - warmup_repetitions.times do - client.search(request) - end - - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - client.search(request) - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: File.basename(DATASET_FILE), - dataset_size: ObjectSpace.memsize_of(dataset), - dataset_n_documents: dataset.length } - index_results!(results, options) - end - - # def mixed_bulk_small(repetitions) - # - # end - # - # def mixed_bulk_large(repetitions) - # - # end - end - end -end diff --git a/profile/benchmarking/data/largedoc.json b/profile/benchmarking/data/largedoc.json deleted file mode 100644 index 41fda56554..0000000000 --- a/profile/benchmarking/data/largedoc.json +++ /dev/null @@ -1 +0,0 @@ -{"text":"@wildfits you're not getting one.....","in_reply_to_status_id":22773233453,"retweet_count":null,"contributors":null,"created_at":"Thu Sep 02 19:38:18 +0000 2010","geo":null,"source":"web","coordinates":null,"in_reply_to_screen_name":"wildfits","truncated":false,"entities":{"user_mentions":[{"indices":[0,9],"screen_name":"wildfits","name":"Mairin Goetzinger","id":41832464}],"urls":[],"hashtags":[]},"retweeted":false,"place":null,"user":{"friends_count":179,"profile_sidebar_fill_color":"7a7a7a","location":"Minneapols, MN/Brookings SD","verified":false,"follow_request_sent":null,"favourites_count":0,"profile_sidebar_border_color":"a3a3a3","profile_image_url":"http://a1.twimg.com/profile_images/1110614677/Screen_shot_2010-08-25_at_10.12.40_AM_normal.png","geo_enabled":false,"created_at":"Sun Aug 17 00:23:13 +0000 2008","description":"graphic designer + foodie, with a love of music, movies, running, design, + the outdoors!","time_zone":"Mountain Time (US & Canada)","url":"http://jessiefarris.com/","screen_name":"jessiekf","notifications":null,"profile_background_color":"303030","listed_count":1,"lang":"en","profile_background_image_url":"http://a3.twimg.com/profile_background_images/133733613/Picture_4.png","statuses_count":1010,"following":null,"profile_text_color":"d9a980","protected":false,"show_all_inline_media":false,"profile_background_tile":true,"name":"Jessie Farris","contributors_enabled":false,"profile_link_color":"363636","followers_count":218,"id":15878015,"profile_use_background_image":true,"utc_offset":-25200},"favorited":false,"in_reply_to_user_id":41832464,"id":22824602300} \ No newline at end of file diff --git a/profile/benchmarking/data/smalldoc.json b/profile/benchmarking/data/smalldoc.json deleted file mode 100644 index c18279eb43..0000000000 --- a/profile/benchmarking/data/smalldoc.json +++ /dev/null @@ -1 +0,0 @@ -{"id": 49434,"cuisine": "mexican"} diff --git a/profile/benchmarking/measurable.rb b/profile/benchmarking/measurable.rb deleted file mode 100644 index 549e300ae8..0000000000 --- a/profile/benchmarking/measurable.rb +++ /dev/null @@ -1,287 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - - module Benchmarking - # Helper functions used by benchmarking tasks - module Measurable - - attr_reader :options - attr_reader :client_adapter - - # The default number of measured repetitions. - # - # @since 7.0.0 - DEFAULT_MEASURED_REPETITIONS = 5 - - # The default number of warmup repetitions. - # - # @since 7.0.0 - DEFAULT_WARMUP_REPETITIONS = 1 - - # The default number of action iterations. - # - # @since 7.0.0 - DEFAULT_ACTION_ITERATIONS = 1 - - # Create a benchmark test. - # - # @example Create a test. - # Benchmarking::Simple.new({ 'repetitions' => { 'warmup' => 1 }}, :patron) - # - # @param [ Hash ] options The options for the benchmarking task. - # @param [ Symbol ] adapter The adapter the client should be configured with. - # - # @since 7.0.0 - def initialize(options = {}, adapter = ::Faraday.default_adapter) - @options = options - @client_adapter = adapter - end - - # Run a benchmark test. - # - # @example Run a test. - # task.run(:ping) - # - # @param [ Symbol ] type The name of the test to run. - # - # @return [ Hash ] The test results document. - # - # @since 7.0.0 - def run(type, opts={}) - send(type, opts) - end - - # Get the nodes info on the elasticsearch server used for the benchmarking tests. - # - # @example Get the nodes info. - # task.nodes_info - # - # @return [ Hash ] The nodes info. - # - # @since 7.0.0 - def nodes_info - client.nodes.info(os: true) if client.ping - end - - # Get the version of the elasticsearch server used for the benchmarking tests. - # - # @example Get the server version. - # task.server_version - # - # @return [ String ] The server version. - # - # @since 7.0.0 - def server_version - client.perform_request('GET', '/').body['version']['number'] if client.ping - end - - # Get the description of the benchmarking task. - # - # @example Get the task description. - # task.description - # - # @return [ String ] The task description. - # - # @since 7.0.0 - def description - @options['description'] - end - - # Get number of measured repetitions. - # - # @example Get the number of measured repetitions. - # task.measured_repetitions - # - # @return [ Numeric ] The number of measured repetitions. - # - # @since 7.0.0 - def measured_repetitions - @options['repetitions']['measured'] || DEFAULT_MEASURED_REPETITIONS - end - - # Get number of warmup repetitions. - # - # @example Get the number of warmup repetitions. - # task.warmup_repetitions - # - # @return [ Numeric ] The number of warmup repetitions. - # - # @since 7.0.0 - def warmup_repetitions - @options['repetitions']['warmup'] || DEFAULT_WARMUP_REPETITIONS - end - - # Get number of iterations of the action. - # - # @example Get the number of iterations of the action. - # task.action_iterations - # - # @return [ Numeric ] The number of action iterations. - # - # @since 7.0.0 - def action_iterations - @options['repetitions']['action_iterations'] || DEFAULT_ACTION_ITERATIONS - end - - private - - attr_reader :adapter - - # The elasticsearch url to use for the tests. - # - # @return [ String ] The Elasticsearch URL to use in tests. - # - # @since 7.0.0 - ELASTICSEARCH_URL = ENV['ELASTICSEARCH_URL'] || "localhost:#{(ENV['TEST_CLUSTER_PORT'] || 9200)}" - - # The username for the results cluster. - # - # @return [ String ] The username for the results cluster. - # - # @since 7.0.0 - ES_RESULT_CLUSTER_USERNAME = ENV['ES_RESULT_CLUSTER_USERNAME'].freeze - - # The password for the results cluster. - # - # @return [ String ] The password for the results cluster. - # - # @since 7.0.0 - ES_RESULT_CLUSTER_PASSWORD = ENV['ES_RESULT_CLUSTER_PASSWORD'].freeze - - # The results cluster url. - # - # @return [ String ] The results cluster url. - # - # @since 7.0.0 - ES_RESULT_CLUSTER_URL = ENV['ES_RESULT_CLUSTER_URL'].freeze - - # The current path. - # - # @return [ String ] The current path. - # - # @since 7.0.0 - CURRENT_PATH = File.expand_path(File.dirname(__FILE__)).freeze - - # The path to data files used in Benchmarking tests. - # - # @return [ String ] Path to Benchmarking test files. - # - # @since 7.0.0 - DATA_PATH = [CURRENT_PATH, 'data'].join('/').freeze - - # The file path and name for the small document. - # - # @return [ String ] The file path and name for the small document. - # - # @since 7.0.0 - SMALL_DOCUMENT = [DATA_PATH, 'smalldoc.json'].join('/').freeze - - # The file path and name for the large document. - # - # @return [ String ] The file path and name for the large document. - # - # @since 7.0.0 - LARGE_DOCUMENT = [DATA_PATH, 'largedoc.json'].join('/').freeze - - # The file path and name for the dataset. - # - # @return [ String ] The file path and name for the dataset. - # - # @since 7.0.0 - DATASET_FILE = [DATA_PATH, 'stackoverflow.json'].join('/').freeze - - # The name of the index to use for benchmark tests. - # - # @return [ String ] The index to use for benchmarking tests. - # - # @since 7.0.0 - INDEX = 'benchmarking-ruby'.freeze - - def load_json_from_file(file_name) - File.open(file_name, "r") do |f| - f.each_line.collect do |line| - JSON.parse(line) - end - end - end - - def with_cleanup - client.indices.delete(index: 'benchmarking-*') - client.indices.create(index: INDEX) unless client.indices.exists?(index: INDEX) - results = yield - client.indices.delete(index: 'benchmarking-*') - results - end - - def client - @client ||= Elasticsearch::Transport::Client.new(host: ELASTICSEARCH_URL, - adapter: adapter, - tracer: nil) - end - - def dataset_slices(slice_size=10000) - @dataset_slices ||= begin - dataset.collect do |d| - { index: { _index: INDEX, _type: '_doc', data: d } } - end.each_slice(slice_size) - end - end - - def dataset - @dataset ||= load_json_from_file(DATASET_FILE) - end - - def small_document - @small_document ||= load_json_from_file(SMALL_DOCUMENT)[0] - end - - def large_document - @large_document ||= load_json_from_file(LARGE_DOCUMENT)[0] - end - - def noop_plugin? - false - end - - def index_results!(results, options = {}) - res = Results.new(self, results, options) - if result_cluster_client.ping - res.index!(result_cluster_client) - puts "#{'*' * 5} Indexed results #{'*' * 5} \n" - else - puts "#{'*' * 5} Results cluster not available, did not index results #{'*' * 5} \n" - end - res.results_doc - rescue => ex - puts "Could not index results, due to #{ex.class}.\n" - puts "Error: #{ex}.\n" - puts "#{ex.backtrace[0..15]}" - end - - def result_cluster_client - @result_cluster_client ||= begin - opts = { host: ES_RESULT_CLUSTER_URL } - opts.merge!(user: ES_RESULT_CLUSTER_USERNAME) if ES_RESULT_CLUSTER_USERNAME - opts.merge!(password: ES_RESULT_CLUSTER_PASSWORD) if ES_RESULT_CLUSTER_PASSWORD - Elasticsearch::Client.new(opts) - end - end - end - end -end diff --git a/profile/benchmarking/results.rb b/profile/benchmarking/results.rb deleted file mode 100644 index ecddb68e8b..0000000000 --- a/profile/benchmarking/results.rb +++ /dev/null @@ -1,254 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -module Elasticsearch - module Benchmarking - - # Class encapsulating formatting and indexing the results from a benchmarking run. - # - # @since 7.0.0 - class Results - - attr_reader :raw_results - - # String constant for unit of time elapsed. - # - # @since 7.0.0 - MILLISECONDS = 'milliseconds'.freeze - - # Create a Results object. - # - # @example Create a results object. - # Benchmarking::Results.new(task, [...], options) - # - # @param [ Elasticsearch::Benchmarking ] task The task that executed the benchmarking run. - # @param [ Array ] results An array of the results. - # @param [ Hash ] options The options. - # - # @since 7.0.0 - def initialize(task, results, options = {}) - @task = task - @raw_results = results.map { |r| r * 1000 } - @options = options - end - - # Index the results document into elasticsearch. - # - # @example Index the results. - # results.index!(client) - # - # @param [ Elasticsearch::Client ] client The client to use to index the results. - # - # @return [ Hash ] The results document. - # - # @since 7.0.0 - def index!(client) - create_index!(client) - client.index(index: index_name, body: results_doc) - results_doc - end - - # The document recording the benchmarking run results, to index into the results cluster. - # - # @example Get the results document. - # results.results_doc - # - # @return [ Hash ] The results document. - # - # @since 7.0.0 - def results_doc - @results_doc ||= begin - { '@timestamp' => Time.now.iso8601, - event: event_doc, - agent: agent_doc, - server: server_doc } - end - end - - private - - attr_reader :options - - DEFAULT_INDEX_NAME = 'benchmarking_results'.freeze - - DEFAULT_METRICS = ['median'].freeze - - CLIENT_NAME = 'elasticsearch-ruby-client'.freeze - - COMPLEXITIES = { Elasticsearch::Benchmarking::Simple => :simple, - Elasticsearch::Benchmarking::Complex => :complex }.freeze - - def action_iterations - options[:action_iterations] - end - - def index_name - options[:index_name] || DEFAULT_INDEX_NAME - end - - def create_index!(client) - unless client.indices.exists?(index: index_name) - client.indices.create(index: index_name) - end - end - - def event_doc - { description: description, - category: category, - action: action, - duration: duration, - statistics: statistics_doc, - repetitions: repetitions_doc }.tap do |doc| - doc.merge!(dataset: dataset, - dataset_details: dataset_details) if dataset - end - end - - def description - @task.description - end - - def category - COMPLEXITIES[@task.class] - end - - def action - @options[:operation] - end - - - def dataset - @options[:dataset] - end - - def dataset_details - { size: @options[:dataset_size], - num_documents: @options[:dataset_n_documents] } - end - - def duration - @options[:duration] * 1000 - end - - def statistics_doc - { unit: MILLISECONDS, - mean: mean, - median: median, - max: max, - min: min, - standard_deviation: standard_deviation - } - end - - def median - raw_results.sort![raw_results.size / 2 - 1] - end - - def mean - raw_results.inject { |sum, el| sum + el }.to_f / raw_results.size - end - - def max - raw_results.max - end - - def min - raw_results.min - end - - def standard_deviation - return 0 if raw_results.size < 2 - Math.sqrt(sample_variance) - end - - def sample_variance - m = mean - sum = raw_results.inject(0) { |sum, i| sum +(i-m)**2 } - sum/(raw_results.length - 1).to_f - end - - def repetitions_doc - { warmup: @task.warmup_repetitions, - measured: @task.measured_repetitions, - iterations: action_iterations } - end - - def agent_doc - { version: Elasticsearch::VERSION, - name: CLIENT_NAME, - git: git_doc, - language: language_doc, - os: client_os_doc, - adapter: adapter } - end - - def adapter - @task.client_adapter - end - - def git_doc - sha = `git rev-parse HEAD` - branch = /\* (.+)/.match(`git branch`)[1] - commit_message = `git log -1 --pretty=%B` - repository = 'elasticsearch-ruby' - - { branch: branch, - sha: sha.chomp, - commit_message: commit_message.gsub(/\n/, ''), - repository: repository.chomp } - end - - def language_doc - version = [ - RUBY_VERSION, - RUBY_PLATFORM, - RbConfig::CONFIG['build'] - ].compact.join(', ') - { runtime_version: version } - end - - def client_os_doc - { platform: platform, - type: type, - architecture: architecture } - end - - def type - (RbConfig::CONFIG && RbConfig::CONFIG['host_os']) ? - RbConfig::CONFIG['host_os'].split('_').first[/[a-z]+/i].downcase : 'unknown' - end - - def architecture - RbConfig::CONFIG['target_cpu'] - end - - def platform - [ - @platform, - RUBY_VERSION, - RUBY_PLATFORM, - RbConfig::CONFIG['build'] - ].compact.join(', ') - end - - def server_doc - { version: @task.server_version, - nodes_info: @task.nodes_info } - end - end - end -end diff --git a/profile/benchmarking/simple.rb b/profile/benchmarking/simple.rb deleted file mode 100644 index 7701f9bc4a..0000000000 --- a/profile/benchmarking/simple.rb +++ /dev/null @@ -1,404 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -require 'objspace' - -module Elasticsearch - module Benchmarking - - # Class encapsulating all settings and functionality for running benchmarking - # tasks making simple requests. - # - # @since 7.0.0 - class Simple - include Measurable - - # Test sending a ping request. - # - # @example Test sending a ping request. - # task.ping(opts) - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results document. - # - # @since 7.0.0 - def ping(opts = {}) - results = [] - action_iterations = 1_000 - - warmup_repetitions.times { client.ping } - - duration = Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - action_iterations.times do - client.ping - end - end - end - end - - options = { duration: duration, - operation: __method__, - action_iterations: action_iterations } - index_results!(results, options) - end - - # Test sending a create_index request. - # - # @example Test sending a create index request. - # task.create_index(opts) - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results document. - # - # @since 7.0.0 - def create_index(opts = {}) - results = [] - action_iterations = 10 - - warmup_repetitions.times do - client.indices.create(index: "benchmarking-#{Time.now.to_f}") - end - - duration = with_cleanup do - Benchmark.realtime do - results = measured_repetitions.times.collect do |i| - index_names = action_iterations.times.collect { |j| (measured_repetitions*i) + j } - Benchmark.realtime do - action_iterations.times do |j| - client.indices.create(index: "benchmarking-#{index_names[j]}") - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - action_iterations: action_iterations } - index_results!(results, options) - end - - # Test sending an index document request for a small document. - # - # @example Test sending an index document request. - # task.index_document_small - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results document. - # - # @since 7.0.0 - def index_document_small(opts={}) - results = [] - document = small_document - action_iterations = 10 - - warmup_repetitions.times do - client.create(index: INDEX, body: document) - end - - duration = with_cleanup do - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - action_iterations.times do - client.create(index: INDEX, body: document) - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: 'small_document', - dataset_size: ObjectSpace.memsize_of(small_document), - dataset_n_documents: 1, - action_iterations: action_iterations } - index_results!(results, options) - end - - # Test sending an index document request for a large document. - # - # @example Test sending an index document request. - # task.index_document_large - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results document. - # - # @since 7.0.0 - def index_document_large(opts={}) - results = [] - document = large_document - action_iterations = 1_000 - - warmup_repetitions.times do - client.create(index: INDEX, body: document) - end - - duration = with_cleanup do - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - action_iterations.times do - client.create(index: INDEX, body: document) - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: 'large_document', - dataset_size: ObjectSpace.memsize_of(large_document), - dataset_n_documents: 1, - action_iterations: action_iterations } - index_results!(results, options) - end - - # Test sending a get document request for a small document. - # - # @example Test sending a get document request. - # Benchmarking::Simple.get_document_small - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results document. - # - # @since 7.0.0 - def get_document_small(opts={}) - results = [] - action_iterations = 1_000 - - duration = with_cleanup do - id = client.create(index: INDEX, body: small_document)['_id'] - warmup_repetitions.times do - client.get(index: INDEX, id: id) - end - - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - action_iterations.times do - client.get(index: INDEX, id: id) - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: 'small_document', - dataset_size: ObjectSpace.memsize_of(small_document), - dataset_n_documents: 1, - action_iterations: action_iterations } - index_results!(results, options) - end - - # Test sending a get document request for a large document. - # - # @example Test sending a get document request. - # Benchmarking::Simple.get_document_large - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results document. - # - # @since 7.0.0 - def get_document_large(opts={}) - duration = 0 - results = [] - action_iterations = 1_000 - - duration = with_cleanup do - id = client.create(index: INDEX, body: large_document)['_id'] - warmup_repetitions.times do - client.get(index: INDEX, id: id) - end - - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - action_iterations.times do - client.get(index: INDEX, id: id) - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: 'large_document', - dataset_size: ObjectSpace.memsize_of(large_document), - dataset_n_documents: 1, - action_iterations: action_iterations } - index_results!(results, options) - end - - # Test sending a search request and retrieving a small document. - # - # @example Test sending a search request for a small document. - # task.search_document_small - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results documents. - # - # @since 7.0.0 - def search_document_small(opts={}) - duration = 0 - results = [] - action_iterations = 1_000 - - duration = with_cleanup do - client.create(index: INDEX, body: small_document) - search_criteria = { match: { cuisine: 'mexican' } } - request = { body: { query: search_criteria } } - if noop_plugin? - Elasticsearch::API.const_set('UNDERSCORE_SEARCH', '_noop_search') - else - request.merge!(index: INDEX) - end - - warmup_repetitions.times do - client.search(request) - end - - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - action_iterations.times do - client.search(request) - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: 'small_document', - dataset_size: ObjectSpace.memsize_of(small_document), - dataset_n_documents: 1, - action_iterations: action_iterations } - index_results!(results, options) - end - - # Test sending a search request and retrieving a large document. - # - # @example Test sending a search request for a large document. - # task.search_document_large - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results documents. - # - # @since 7.0.0 - def search_document_large(opts={}) - results = [] - action_iterations = 1_000 - - duration = with_cleanup do - client.create(index: INDEX, body: large_document) - search_criteria = { match: { 'user.lang': 'en' } } - request = { body: { query: search_criteria } } - if noop_plugin? - Elasticsearch::API.const_set('UNDERSCORE_SEARCH', '_noop_search') - else - request.merge!(index: INDEX) - end - warmup_repetitions.times do - client.search(request) - end - - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - action_iterations.times do - client.search(request) - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: 'large_document', - dataset_size: ObjectSpace.memsize_of(large_document), - dataset_n_documents: 1, - action_iterations: action_iterations } - index_results!(results, options) - end - - # Test sending a update request for a small document. - # - # @example Test sending an update request for a small document. - # Benchmarking::Simple.update_document - # - # @param [ Hash ] opts The test run options. - # - # @results [ Hash ] The results documents. - # - # @since 7.0.0 - def update_document(opts={}) - results = [] - action_iterations = 1_000 - - duration = with_cleanup do - document = small_document - id = client.create(index: INDEX, body: document)['_id'] - field = document.find { |k,v| k != 'id' && v.is_a?(String) }.first - - warmup_repetitions.times do |i| - client.update(index: INDEX, - id: id, - body: { doc: { field: "#{document[field]}-#{i}" } }) - end - - Benchmark.realtime do - results = measured_repetitions.times.collect do - Benchmark.realtime do - action_iterations.times do |i| - client.update(index: INDEX, - id: id, - body: { doc: { field: "#{document[field]}-#{i}" } }) - end - end - end - end - end - - options = { duration: duration, - operation: __method__, - dataset: 'small_document', - dataset_size: ObjectSpace.memsize_of(small_document), - dataset_n_documents: 1, - action_iterations: action_iterations } - index_results!(results, options) - end - end - end -end diff --git a/profile/matrix.yml b/profile/matrix.yml deleted file mode 100644 index d331ff58d6..0000000000 --- a/profile/matrix.yml +++ /dev/null @@ -1,16 +0,0 @@ -- description: Basic benchmark, 5 repetitions - repetitions: - warmup: 1 - measured: 5 - metrics: - - mean - - median - - max - - min - -- description: Ten Repetitions - repetitions: - warmup: 5 - measured: 10 - metrics: - - mean \ No newline at end of file diff --git a/rake_tasks/automation.rake b/rake_tasks/automation.rake index 6edce27267..4320bb508b 100644 --- a/rake_tasks/automation.rake +++ b/rake_tasks/automation.rake @@ -122,4 +122,9 @@ namespace :automation do File.open(file, 'w') { |f| f.puts content } end end + + desc 'Show current client version' + task :version do + puts Elasticsearch::VERSION + end end diff --git a/rake_tasks/docker_tasks.rake b/rake_tasks/docker_tasks.rake index b26af2b60b..478ecd7d42 100644 --- a/rake_tasks/docker_tasks.rake +++ b/rake_tasks/docker_tasks.rake @@ -16,40 +16,39 @@ # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. -require 'mkmf' -namespace :docker do +require 'mkmf' # For find_executable + +namespace :es do desc <<~DOC Start Elasticsearch in a Docker container. Default: - rake docker:start[version] + rake es:start[version] E.g.: - rake docker:start[7.x-SNAPSHOT] + rake es:start[9.x-SNAPSHOT] To start the container with Platinum, pass it in as a parameter: - rake docker:start[7.x-SNAPSHOT,platinum] + rake es:start[9.x-SNAPSHOT,platinum] DOC task :start, [:version, :suite] do |_, params| abort 'Docker not installed' unless find_executable 'docker' - abort 'You need to set a version, e.g. rake docker:start[7.x-SNAPSHOT]' unless params[:version] + abort 'You need to set a version, e.g. rake docker:start[9.x-SNAPSHOT]' unless params[:version] test_suite = params[:suite] || 'free' system("STACK_VERSION=#{params[:version]} TEST_SUITE=#{test_suite} ./.buildkite/run-elasticsearch.sh") end -end -namespace :es do desc <<~DOC Start Elasticsearch docker container (shortcut), reads STACK_VERSION from buildkite pipeline DOC task :up do - version = File.read('./.buildkite/pipeline.yml'). - split("\n"). - select { |a| a.include? 'STACK_VERSION' } - .first - .strip - .gsub('STACK_VERSION: ','') - Rake.application.invoke_task("docker:start[#{version}]") + version = File.read('./.buildkite/pipeline.yml') + .split("\n") + .select { |a| a.include? 'STACK_VERSION' } + .first + .strip + .gsub('STACK_VERSION: ', '') + Rake.application.invoke_task("es:start[#{version}, platinum]") end end diff --git a/rake_tasks/elasticsearch_tasks.rake b/rake_tasks/elasticsearch_tasks.rake index a38989ab85..9d7deb50a5 100644 --- a/rake_tasks/elasticsearch_tasks.rake +++ b/rake_tasks/elasticsearch_tasks.rake @@ -44,9 +44,8 @@ namespace :es do desc 'Automatically update to latest version' task :autoupdate_version do require 'tempfile' - branch = `git branch --show-current`.strip - url = "https://snapshots.elastic.co/latest/#{branch}.json" + url = "https://artifacts-snapshot.elastic.co/elasticsearch/latest/#{branch}.json" file = Tempfile.new('version') download_file!(url, file) version = JSON.parse(file.read)['version'] diff --git a/rake_tasks/update_version.rake b/rake_tasks/update_version.rake deleted file mode 100644 index bd59232768..0000000000 --- a/rake_tasks/update_version.rake +++ /dev/null @@ -1,111 +0,0 @@ -# Licensed to Elasticsearch B.V. under one or more contributor -# license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright -# ownership. Elasticsearch B.V. licenses this file to you under -# the Apache License, Version 2.0 (the "License"); you may -# not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -desc <<-DESC - Update Rubygems versions in version.rb and *.gemspec files - - Example: - - $ rake update_version[5.0.0,5.0.1] -DESC -task :update_version, :old, :new do |_, args| - require 'ansi' - - puts '[!!!] Required argument [old] missing'.ansi(:red) unless args[:old] - puts '[!!!] Required argument [new] missing'.ansi(:red) unless args[:new] - - files = Dir['**/**/version.rb','**/**/*.gemspec'] - - longest_line = files.map(&:size).max - - puts "\n", '= FILES '.ansi(:faint) + ('='*92).ansi(:faint), "\n" - - files.each do |file| - begin - content = File.read(file) - if content.match Regexp.new(args[:old]) - content.gsub! Regexp.new(args[:old]), args[:new] - puts "+ [#{file}]".ansi(:green).ljust(longest_line+20) + " [#{args[:old]}] -> [#{args[:new]}]".ansi(:green,:bold) - File.open(file, 'w') { |f| f.puts content } - else - puts "- [#{file}]".ansi(:yellow).ljust(longest_line+20) + " -".ansi(:faint,:strike) - end - rescue Exception => e - puts "[!!!] #{e.class} : #{e.message}".ansi(:red,:bold) - raise e - end - end -end - -task :update_changelog do - puts "\n\n", '= CHANGELOG '.ansi(:faint) + ('='*88).ansi(:faint), "\n" - - log = `git --no-pager log --reverse --no-color --pretty='* %s' HEAD --not v#{args[:old]} elasticsearch*`.split("\n") - - puts log.join("\n") - - log_entries = {} - log_entries[:client] = log.select { |l| l =~ /\[CLIENT\]/ } - log_entries[:api] = log.select { |l| l =~ /\[API\]/ } - - changelog = File.read(File.open('CHANGELOG.md', 'r')) - - changelog_update = '' - - if log.any? { |l| l =~ /CLIENT|API/ } - changelog_update << "## #{args[:new]}\n\n" - end - - unless log_entries[:client].empty? - changelog_update << "### Client\n\n" - changelog_update << log_entries[:client] - .map { |l| l.gsub /\[CLIENT\] /, '' } - .map { |l| "#{l}" } - .join("\n") - changelog_update << "\n\n" - end - - unless log_entries[:api].empty? - changelog_update << "### API\n\n" - changelog_update << log_entries[:api] - .map { |l| l.gsub /\[API\] /, '' } - .map { |l| "#{l}" } - .join("\n") - changelog_update << "\n\n" - end - - File.open('CHANGELOG.md', 'w+') { |f| f.write changelog_update and f.write changelog } - - puts "\n\n", "= DIFF ".ansi(:faint) + ('='*93).ansi(:faint) - - diff = `git --no-pager diff --patch --word-diff=color --minimal elasticsearch*`.split("\n") - - puts diff - .reject { |l| l =~ /^\e\[1mdiff \-\-git/ } - .reject { |l| l =~ /^\e\[1mindex [a-z0-9]{7}/ } - .reject { |l| l =~ /^\e\[1m\-\-\- i/ } - .reject { |l| l =~ /^\e\[36m@@/ } - .map { |l| l =~ /^\e\[1m\+\+\+ w/ ? "\n#{l} " + '-'*(104-l.size) : l } - .join("\n") - - puts "\n\n", '= COMMIT '.ansi(:faint) + ('='*91).ansi(:faint), "\n" - - puts 'git add CHANGELOG.md elasticsearch*', - "git commit --verbose --message='Release #{args[:new]}' --edit", - 'rake release', - '' -end