-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
[FIX] Serializers can now be defined *before* Rails initializes and cache store will be correctly set #1478
Conversation
Fixes #923 |
@@ -89,6 +89,10 @@ def fragmented(serializer) | |||
# https://github.com/rails-api/active_model_serializers/pull/1249#issuecomment-146567837 | |||
def cache(options = {}) | |||
self._cache = ActiveModelSerializers.config.cache_store if ActiveModelSerializers.config.perform_caching | |||
serializer = self | |||
ActiveSupport.on_load(:action_controller) do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
duplicate since we can't rely on an app loading a controller
@@ -89,6 +89,10 @@ def fragmented(serializer) | |||
# https://github.com/rails-api/active_model_serializers/pull/1249#issuecomment-146567837 | |||
def cache(options = {}) | |||
self._cache = ActiveModelSerializers.config.cache_store if ActiveModelSerializers.config.perform_caching |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The real problem is that calling this method should enable the cache via sentinel value, rather than setting the cache store dependent on an controller attribute. Each serializer could still have its own cache_store (though I'm not sure how often that would be used, and it causes extra complexity here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Older versions of AMS used a method perform_caching
named same as rails. I think that might be a good idea
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
easiest impl, have a class attribute perform_caching
that acts as the sentinel. That was we don't have to change the api much... caching can still be turning on/off per serializer, but configured at load and disabled/enabled before use. (that's the impl that was in 0.8 #1568 )
What about something like? def _cache
return @cache if @cache_set
@cache_set = true
@cache = ActiveModelSerializers.config.cache_store if ActiveModelSerializers.config.perform_caching
end
def _cache=(cache)
@cache_set = true
@cache = cache
end AFAIK, the |
Also a problem that _cache doubled as the serializer cache and a sentinel
|
090f24a
to
4d6082f
Compare
ignore 4d6082f.. more objects and loses performance, oops |
@bf4, could you rebase this and fix the rubocop warnings? |
I thought this was broken and I gave up for now B mobile phone
|
Does the issue still need to be resolved? |
@NullVoxPopuli absolutely yes |
@NullVoxPopuli @remear @groyoh I believe I fixed it! There's definitely a bit of jankyness, so please comment if you see a better way. A summary of our needs:
Summary of problems:
Summary of solution:
More details in PR description |
bdd6cc4
to
a7eca81
Compare
BenchmarkingBenchmarks confirm no performance hit. I also seem to be generating 3 less objects. BeforeMaster HEAD db87f8d bin/bench -r 5
caching on: caching serializers: gc off 634.3654607311329/ips; 1854 objects
caching off: caching serializers: gc off 614.575522426943/ips; 1854 objects
caching on: non-caching serializers: gc off 782.609603827895/ips; 1392 objects
caching off: non-caching serializers: gc off 706.3208987369165/ips; 1392 objects
caching on: caching serializers: gc off 626.183018453044/ips; 1854 objects
caching off: caching serializers: gc off 611.131393623337/ips; 1854 objects
caching on: non-caching serializers: gc off 800.8413437916081/ips; 1392 objects
caching off: non-caching serializers: gc off 730.1991428832154/ips; 1392 objects
caching on: caching serializers: gc off 635.9595295750097/ips; 1854 objects
caching off: caching serializers: gc off 613.6265970839457/ips; 1854 objects
caching on: non-caching serializers: gc off 793.4097406411736/ips; 1392 objects
caching off: non-caching serializers: gc off 727.5700645544579/ips; 1392 objects
caching on: caching serializers: gc off 645.498710493511/ips; 1854 objects
caching off: caching serializers: gc off 618.2892779001014/ips; 1854 objects
caching on: non-caching serializers: gc off 784.3447828298285/ips; 1392 objects
caching off: non-caching serializers: gc off 737.2795995214526/ips; 1392 objects
caching on: caching serializers: gc off 641.8325554022904/ips; 1854 objects
caching off: caching serializers: gc off 615.5090793528184/ips; 1854 objects
caching on: non-caching serializers: gc off 780.1478229085403/ips; 1392 objects
caching off: non-caching serializers: gc off 751.8133968350586/ips; 1392 objects
Benchmark results:
{
"commit_hash": "db87f8d",
"version": "0.10.0.rc4",
"rails_version": "4.0.13",
"benchmark_run[environment]": "2.2.3p173",
"runs": [
{
"benchmark_type[category]": "caching on: caching serializers: gc off",
"benchmark_run[result][iterations_per_second]": 645.499,
"benchmark_run[result][total_allocated_objects_per_iteration]": 1854
},
{
"benchmark_type[category]": "caching off: caching serializers: gc off",
"benchmark_run[result][iterations_per_second]": 618.289,
"benchmark_run[result][total_allocated_objects_per_iteration]": 1854
},
{
"benchmark_type[category]": "caching on: non-caching serializers: gc off",
"benchmark_run[result][iterations_per_second]": 800.841,
"benchmark_run[result][total_allocated_objects_per_iteration]": 1392
},
{
"benchmark_type[category]": "caching off: non-caching serializers: gc off",
"benchmark_run[result][iterations_per_second]": 751.813,
"benchmark_run[result][total_allocated_objects_per_iteration]": 1392
}
]
} AfterPR HEAD a7eca81 bin/bench -r 5
caching on: caching serializers: gc off 588.0665690387696/ips; 1851 objects
caching off: caching serializers: gc off 571.1490068727436/ips; 1851 objects
caching on: non-caching serializers: gc off 754.977344046592/ips; 1392 objects
caching off: non-caching serializers: gc off 724.5287137156264/ips; 1392 objects
caching on: caching serializers: gc off 605.7295792722412/ips; 1851 objects
caching off: caching serializers: gc off 600.8992787976632/ips; 1851 objects
caching on: non-caching serializers: gc off 782.7515015587951/ips; 1392 objects
caching off: non-caching serializers: gc off 734.3344036062412/ips; 1392 objects
caching on: caching serializers: gc off 642.4116811888662/ips; 1851 objects
caching off: caching serializers: gc off 602.0263238031482/ips; 1851 objects
caching on: non-caching serializers: gc off 792.3897748109852/ips; 1392 objects
caching off: non-caching serializers: gc off 748.3909836691489/ips; 1392 objects
caching on: caching serializers: gc off 625.5546234938934/ips; 1851 objects
caching off: caching serializers: gc off 619.9850146085776/ips; 1851 objects
caching on: non-caching serializers: gc off 775.00739096189/ips; 1392 objects
caching off: non-caching serializers: gc off 751.4049208005396/ips; 1392 objects
caching on: caching serializers: gc off 635.8718384084705/ips; 1851 objects
caching off: caching serializers: gc off 612.727137954216/ips; 1851 objects
caching on: non-caching serializers: gc off 779.1109767055024/ips; 1392 objects
caching off: non-caching serializers: gc off 743.0832290755119/ips; 1392 objects
Benchmark results:
{
"commit_hash": "a7eca81",
"version": "0.10.0.rc4",
"rails_version": "4.0.13",
"benchmark_run[environment]": "2.2.3p173",
"runs": [
{
"benchmark_type[category]": "caching on: caching serializers: gc off",
"benchmark_run[result][iterations_per_second]": 642.412,
"benchmark_run[result][total_allocated_objects_per_iteration]": 1851
},
{
"benchmark_type[category]": "caching off: caching serializers: gc off",
"benchmark_run[result][iterations_per_second]": 619.985,
"benchmark_run[result][total_allocated_objects_per_iteration]": 1851
},
{
"benchmark_type[category]": "caching on: non-caching serializers: gc off",
"benchmark_run[result][iterations_per_second]": 792.39,
"benchmark_run[result][total_allocated_objects_per_iteration]": 1392
},
{
"benchmark_type[category]": "caching off: non-caching serializers: gc off",
"benchmark_run[result][iterations_per_second]": 751.405,
"benchmark_run[result][total_allocated_objects_per_iteration]": 1392
}
]
} |
# We're using class variables here because it is a class attribute | ||
# that is globally true for the `ActiveModel::Serializer` class; i.e. neither per subclass nor inherited. | ||
# | ||
# We're not using a class_attribute because of the special behavior in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thanks for the explanation! I like to read the thoughts behind decisions
# This is to allow us to have a global config that can be set any time before | ||
# `perform_caching` is called. | ||
# | ||
# One downside of this, is that subsequent setting of the global config will not change |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just because the class var is memoized?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, class variables are considered an anti-pattern in Ruby. This is distinct from class instance variables. Go and learn :)
Maybe something to look at, not sure if this'll work. But, re: delegating to the config, in active_model_serializers.rb#config, the Serializer class should autoload when the config method is called on, yeah? in lib/ams/serializers.rb: require 'active_model/serializer/collection_serializer'
require 'active_model/serializer/array_serializer'
require 'active_model/serializer/error_serializer'
require 'active_model/serializer/errors_serializer'
require 'active_model/serializer/include_tree'
require 'active_model/serializer/associations'
require 'active_model/serializer/attributes'
require 'active_model/serializer/caching'
require 'active_model/serializer/configuration' what if configuration was at the top? would that help? or is the loading race condition elsewhere? |
nope. we're talking about when Rails is initialized vs. when the serializers are loaded vs. when the controller is first loaded. That's not a race condition. It's strictly determined by load order. More details in the PR description |
end | ||
end | ||
|
||
def cache_enabled? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
Benchmark remains consistent with #1478 (comment):
|
@@ -43,7 +43,8 @@ Rake::TestTask.new do |t| | |||
t.libs << 'test' | |||
t.libs << 'lib' | |||
t.test_files = FileList['test/**/*_test.rb'] | |||
t.ruby_opts = ['-w -r./test/test_helper.rb'] | |||
t.ruby_opts = ['-r./test/test_helper.rb'] | |||
t.ruby_opts << ' -w' unless ENV['NO_WARN'] == 'true' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be already on master? Weird that it still shows in the diff.
Looks good to me 🎉 |
Update: #1478 (comment) and #1478 (comment) #1478 (comment)
This is kind of a dumb fix for caching that demonstrates the
need in Rails for Serializer::_cache to be set after the controller is loaded
when Serializer::cache has already been called.
Notes on the issue