From f48798e85c890c9be6873e427137b8c8cde4171d Mon Sep 17 00:00:00 2001 From: Dmitry Krasnoukhov Date: Thu, 7 Nov 2019 16:42:00 +0200 Subject: [PATCH] Memory usage improvements --- jsonapi-resources.gemspec | 1 + lib/jsonapi/link_builder.rb | 23 +++++++++++++---------- lib/jsonapi/resource.rb | 7 +++++-- lib/jsonapi/resource_serializer.rb | 2 +- test/benchmark/request_benchmark.rb | 4 ++-- test/test_helper.rb | 4 ++++ 6 files changed, 26 insertions(+), 15 deletions(-) diff --git a/jsonapi-resources.gemspec b/jsonapi-resources.gemspec index 381107ad5..10dfb0aff 100644 --- a/jsonapi-resources.gemspec +++ b/jsonapi-resources.gemspec @@ -27,6 +27,7 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'pry' spec.add_development_dependency 'concurrent-ruby-ext' spec.add_development_dependency 'database_cleaner' + spec.add_development_dependency 'memory_profiler' spec.add_dependency 'activerecord', '>= 4.1' spec.add_dependency 'railties', '>= 4.1' spec.add_dependency 'concurrent-ruby' diff --git a/lib/jsonapi/link_builder.rb b/lib/jsonapi/link_builder.rb index 99cd6223c..992e52f77 100644 --- a/lib/jsonapi/link_builder.rb +++ b/lib/jsonapi/link_builder.rb @@ -105,16 +105,19 @@ def format_route(route) end def formatted_module_path_from_class(klass) - scopes = if @engine - module_scopes_from_class(klass)[1..-1] - else - module_scopes_from_class(klass) - end - - unless scopes.empty? - "/#{ scopes.map {|scope| format_route(scope.to_s.underscore)}.compact.join('/') }/" - else - "/" + @_module_path_cache ||= {} + @_module_path_cache[klass] ||= begin + scopes = if @engine + module_scopes_from_class(klass)[1..-1] + else + module_scopes_from_class(klass) + end + + unless scopes.empty? + "/#{ scopes.map {|scope| format_route(scope.to_s.underscore)}.compact.join('/') }/" + else + "/" + end end end diff --git a/lib/jsonapi/resource.rb b/lib/jsonapi/resource.rb index 2f377a67f..84b2a96fb 100644 --- a/lib/jsonapi/resource.rb +++ b/lib/jsonapi/resource.rb @@ -5,6 +5,9 @@ module JSONAPI class Resource include Callbacks + DEFAULT_ATTRIBUTE_OPTIONS = { format: :default }.freeze + MODULE_PATH_REGEXP = /::[^:]+\Z/.freeze + attr_reader :context define_jsonapi_resources_callbacks :create, @@ -555,7 +558,7 @@ def attribute(attribute_name, options = {}) end def default_attribute_options - { format: :default } + DEFAULT_ATTRIBUTE_OPTIONS end def relationship(*attrs) @@ -1133,7 +1136,7 @@ def module_path if name == 'JSONAPI::Resource' '' else - name =~ /::[^:]+\Z/ ? ($`.freeze.gsub('::', '/') + '/').underscore : '' + name =~ MODULE_PATH_REGEXP ? ($`.freeze.gsub('::', '/') + '/').underscore : '' end end diff --git a/lib/jsonapi/resource_serializer.rb b/lib/jsonapi/resource_serializer.rb index 3bdf4dd49..37a573552 100644 --- a/lib/jsonapi/resource_serializer.rb +++ b/lib/jsonapi/resource_serializer.rb @@ -287,7 +287,6 @@ def relationships_hash(source, fetchable_fields, include_directives = {}) include_linkage = ia && ia[:include] include_linked_children = ia && !ia[:include_related].empty? - options = { filters: ia && ia[:include_filters] || {} } if field_set.include?(name) ro = relationship_object(source, relationship, include_linkage) hash[format_key(name)] = ro unless ro.blank? @@ -300,6 +299,7 @@ def relationships_hash(source, fetchable_fields, include_directives = {}) resources = if source.preloaded_fragments.has_key?(format_key(name)) source.preloaded_fragments[format_key(name)].values else + options = { filters: ia && ia[:include_filters] || {} } [source.public_send(name, options)].flatten(1).compact end resources.each do |resource| diff --git a/test/benchmark/request_benchmark.rb b/test/benchmark/request_benchmark.rb index d42d3a695..b71cdf0b5 100644 --- a/test/benchmark/request_benchmark.rb +++ b/test/benchmark/request_benchmark.rb @@ -7,7 +7,7 @@ def setup end def bench_large_index_request_uncached - 10.times do + 100.times do assert_jsonapi_get '/api/v2/books?include=bookComments,bookComments.author' end end @@ -15,7 +15,7 @@ def bench_large_index_request_uncached def bench_large_index_request_caching cache = ActiveSupport::Cache::MemoryStore.new with_resource_caching(cache) do - 10.times do + 100.times do assert_jsonapi_get '/api/v2/books?include=bookComments,bookComments.author' end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 8f8ba5a1f..ee2b36674 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -24,6 +24,7 @@ require 'minitest/mock' require 'jsonapi-resources' require 'pry' +require 'memory_profiler' require File.expand_path('../helpers/value_matchers', __FILE__) require File.expand_path('../helpers/assertions', __FILE__) @@ -639,6 +640,9 @@ def self.run_one_method(klass, method_name, reporter) end end puts + if ENV["MEMORY_PROFILER"] + MemoryProfiler.report(allow_files: 'lib/jsonapi') { super(klass, method_name, reporter) }.pretty_print + end end end