Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Eager load all API specs and reduce memory usage #285

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 16 additions & 26 deletions lib/elastomer_client/client/rest_api_spec.rb
Original file line number Diff line number Diff line change
@@ -1,45 +1,35 @@
# frozen_string_literal: true

require_relative "rest_api_spec/api_spec"
require_relative "rest_api_spec/rest_api"

# These are all eager loaded, which may be wasteful for unused versions but is
# preferable for copy-on-write and to avoid loading at runtime.

require_relative "rest_api_spec/api_spec_v5_6"
require_relative "rest_api_spec/api_spec_v8_7"

module ElastomerClient
class Client

# Provides access to the versioned REST API specs for Elasticsearch.
module RestApiSpec
API_SPECS = {
"5_6" => ApiSpecV5_6.new,
"8_7" => ApiSpecV8_7.new,
}.freeze

# Returns an ApiSpec instance for the given Elasticsearcion version. This
# method will load the ApiSpec version class if it has not already been
# defined. This prevents bloat by only loading the version specs that are
# needed.
#
# Because of this lazy loading, this method is _not_ thread safe.
# Returns an ApiSpec instance for the given Elasticsearcion version.
#
# version - the Elasticsearch version String
#
# Returns the requested ApiSpec version if available
def self.api_spec(version)
classname = "ApiSpecV#{to_class_version(version)}"
load_api_spec(version) if !self.const_defined? classname
self.const_get(classname).new
end

# Internal: Load the specific ApiSpec version class for the given version.
def self.load_api_spec(version)
path = File.expand_path("../rest_api_spec/api_spec_v#{to_class_version(version)}.rb", __FILE__)
if File.exist? path
load path
else
short_version = version.to_s.split(".").slice(0, 2).join("_")
API_SPECS.fetch(short_version) do
raise RuntimeError, "Unsupported REST API spec version: #{version}"
end
end

# Internal: Convert a dotted version String into an underscore format
# suitable for use in Ruby class names.
def self.to_class_version(version)
version.to_s.split(".").slice(0, 2).join("_")
end
end
end
end

require_relative "rest_api_spec/api_spec"
require_relative "rest_api_spec/rest_api"
4 changes: 1 addition & 3 deletions lib/elastomer_client/client/rest_api_spec/api_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ class ApiSpec
attr_reader :common_params

def initialize
@rest_apis ||= {}
@common_params ||= {}
@common_params_set = Set.new(@common_params.keys)
@common_params_set = Set.new(@common_params)
end

# Given an API descriptor name and a set of request parameters, select those
Expand Down
2,707 changes: 1,353 additions & 1,354 deletions lib/elastomer_client/client/rest_api_spec/api_spec_v5_6.rb

Large diffs are not rendered by default.

5,523 changes: 2,762 additions & 2,761 deletions lib/elastomer_client/client/rest_api_spec/api_spec_v8_7.rb

Large diffs are not rendered by default.

14 changes: 7 additions & 7 deletions lib/elastomer_client/client/rest_api_spec/rest_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ def body?
class Url
attr_reader :path
attr_reader :paths
attr_reader :parts
attr_reader :params
attr_reader :parts_keys
attr_reader :params_keys

def initialize(path:, paths: [], parts: {}, params: {})
def initialize(path:, paths: [], parts: [], params: [])
@path = path
@paths = Array(paths)
@parts = parts
@params = params
@parts_keys = parts
@params_keys = params

@parts_set = Set.new(@parts.keys)
@params_set = Set.new(@params.keys)
@parts_set = Set.new(@parts_keys)
@params_set = Set.new(@params_keys)
end

def select_parts(from:)
Expand Down
30 changes: 15 additions & 15 deletions script/generate-rest-api-spec
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
require "erb"
require "rubygems"
require "bundler/setup"
require "json"

$LOAD_PATH.unshift "lib"
require "elastomer_client/client"
require "elastomer_client/version_support"

class RestApiSpecGenerator
Expand All @@ -36,9 +36,8 @@ class RestApiSpecGenerator
# elasticsearch version.
def run
setup
File.open(ruby_spec_filename, "w") do |fd|
fd.puts ERB.new(DATA.read, trim_mode: "-").result(binding)
end
ruby = ERB.new(DATA.read, trim_mode: "-").result(binding)
File.write(ruby_spec_filename, ruby)
ensure
reset
end
Expand All @@ -59,7 +58,7 @@ class RestApiSpecGenerator
Dir.glob("#{WORKING_DIR}/rest-api-spec/src/main/resources/rest-api-spec/api/*.json").sort.each do |filename|
next if filename =~ /\/_common\.json\Z/

hash = MultiJson.load(File.read(filename))
hash = JSON.parse(File.read(filename))
key = hash.keys.first
value = hash.values.first
yield(key, value)
Expand All @@ -71,7 +70,7 @@ class RestApiSpecGenerator
def each_common
filename = "#{WORKING_DIR}/rest-api-spec/src/main/resources/rest-api-spec/api/_common.json"
if File.exist? filename
hash = MultiJson.load(File.read(filename))
hash = JSON.parse(File.read(filename))
hash["params"].each { |k, v| yield(k, v) }
end
end
Expand Down Expand Up @@ -160,6 +159,7 @@ end
puts RestApiSpecGenerator.new(*ARGV).run

__END__
# frozen_string_literal: true
# Generated REST API spec file - DO NOT EDIT!
# Date: <%= Time.now.strftime("%Y-%m-%d") %>
# ES version: <%= version %>
Expand All @@ -178,29 +178,29 @@ module ElastomerClient::Client::RestApiSpec
path: "<%= generate_path(url) %>",
paths: <%= generate_paths(url) %>,
<% if (parts = generate_parts(url)) && !parts.empty? -%>
parts: {
parts: [
<% parts.each do |k,v| -%>
"<%= k %>" => <%= v.to_s %>,
"<%= k %>",
<% end -%>
},
],
<% end -%>
<% params = generate_params(data) -%>
<% if !params.nil? && !params.empty? -%>
params: {
params: [
<% params.each do |k,v| -%>
"<%= k %>" => <%= v.to_s %>,
"<%= k %>",
<% end -%>
}
]
<% end -%>
}
),
<% end -%>
}
@common_params = {
@common_params = [
<% each_common do |k,v| -%>
"<%= k %>" => <%= v.to_s %>,
"<%= k %>",
<% end -%>
}
]
super
end
end
Expand Down
25 changes: 11 additions & 14 deletions test/client/rest_api_spec/rest_api_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@
url: {
path: "/_cluster/state",
paths: ["/_cluster/state", "/_cluster/state/{metric}", "/_cluster/state/{metric}/{index}"],
parts: {
"index" => {"type"=>"list", "description"=>"A comma-separated list of index names; use `_all` or empty string to perform the operation on all indices"},
"metric" => {"type"=>"list", "options"=>["_all", "blocks", "metadata", "nodes", "routing_table", "routing_nodes", "master_node", "version"], "description"=>"Limit the information returned to the specified metrics"},
},
params: {
"local" => {"type"=>"boolean", "description"=>"Return local information, do not retrieve the state from master node (default: false)"},
"master_timeout" => {"type"=>"time", "description"=>"Specify timeout for connection to master"},
"flat_settings" => {"type"=>"boolean", "description"=>"Return settings in flat format (default: false)"},
"ignore_unavailable" => {"type"=>"boolean", "description"=>"Whether specified concrete indices should be ignored when unavailable (missing or closed)"},
"allow_no_indices" => {"type"=>"boolean", "description"=>"Whether to ignore if a wildcard indices expression resolves into no concrete indices. (This includes `_all` string or when no indices have been specified)"},
"expand_wildcards" => {"type"=>"enum", "options"=>["open", "closed", "none", "all"], "default"=>"open", "description"=>"Whether to expand wildcard expression to concrete indices that are open, closed or both."},
}
parts: ["index", "metric"],
params: [
"local",
"master_timeout",
"flat_settings",
"ignore_unavailable",
"allow_no_indices",
"expand_wildcards"
]
}
end

Expand Down Expand Up @@ -88,11 +85,11 @@
end

it "accesses the path parts" do
assert_equal %w[index metric], @rest_api.url.parts.keys
assert_equal %w[index metric], @rest_api.url.parts_keys
end

it "accesses the request params" do
assert_equal %w[local master_timeout flat_settings ignore_unavailable allow_no_indices expand_wildcards], @rest_api.url.params.keys
assert_equal %w[local master_timeout flat_settings ignore_unavailable allow_no_indices expand_wildcards], @rest_api.url.params_keys
end
end
end
Loading