# SPDX-License-Identifier: Apache-2.0 # # The OpenSearch Contributors require contributions made to # this file be licensed under the Apache-2.0 license or a # compatible open source license. # # Modifications Copyright OpenSearch Contributors. See # GitHub history for details. # # 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 OpenSearch 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 opensearch 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 opensearch 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 opensearch url to use for the tests. # # @return [ String ] The OpenSearch URL to use in tests. # # @since 7.0.0 OPENSEARCH_URL = ENV['OPENSEARCH_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 ||= OpenSearch::Transport::Client.new(host: OPENSEARCH_URL, adapter: adapter, tracer: nil) end def dataset_slices(slice_size=10000) @dataset_slices ||= begin dataset.collect do |d| { index: { _index: INDEX, 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 OpenSearch::Client.new(opts) end end end end end