Skip to content

Commit dfd215d

Browse files
sjmogshishirmk
authored andcommitted
91 allow includes strings (#93)
* add hash benchmarking to performance tests * Add missing attribute in README example * Disable GC before doing performance test * Enable oj to AM for fair benchmark test * add information on performance methodology * add oss metadata * Make an error that demonstrates [Issue * Simple RSpec test that fails with a non-empty string but passes with a non-empty symbol * To run the test, rspec spec/lib/object_serializer_spec.rb * Map includes to symbols if they are provided as strings * Includes would fail with an ArgumentError unless they were explicitly provided as symbols (see #97) * This is solved by mapping the strings to symbols in the ObjectSerializer initializer * No real impact on performance here
1 parent 1ff5fe3 commit dfd215d

6 files changed

Lines changed: 56 additions & 2 deletions

File tree

OSSMETADATA

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
osslifecycle=active

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ A lightning fast [JSON:API](http://jsonapi.org/) serializer for Ruby Objects.
66

77
# Performance Comparison
88

9-
We compare serialization times with Active Model Serializer as part of RSpec performance tests included on this library. We want to ensure that with every change on this library, serialization time is at least `25 times` faster than Active Model Serializers on up to current benchmark of 1000 records.
9+
We compare serialization times with Active Model Serializer as part of RSpec performance tests included on this library. We want to ensure that with every change on this library, serialization time is at least `25 times` faster than Active Model Serializers on up to current benchmark of 1000 records. Please read the [performance document](https://github.com/Netflix/fast_jsonapi/blob/master/performance_methodology.md) for any questions related to methodology.
1010

1111
## Benchmark times for 250 records
1212

lib/fast_jsonapi/object_serializer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def process_options(options)
8484
@meta = options[:meta]
8585

8686
if options[:include].present?
87-
@includes = options[:include].delete_if(&:blank?)
87+
@includes = options[:include].delete_if(&:blank?).map(&:to_sym)
8888
validate_includes!(@includes)
8989
end
9090
end

performance_methodology.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Performance using Fast JSON API
2+
3+
We have been getting a few questions on Github about [Fast JSON API’s](https://github.com/Netflix/fast_jsonapi) performance statistics and the methodology used to measure the performance. This article is an attempt at addressing this aspect of the gem.
4+
5+
## Prologue
6+
7+
With use cases like infinite scroll on complex models and bulk update on index pages, we started observing performance degradation on our Rails APIs. Our first step was to enable instrumentation and then tune for performance. We realized that, on average, more than 50% of the time was being spent on AMS serialization. At the same time, we had a couple of APIs that were simply proxying requests on top of a non-Rails, non-JSON API endpoint. Guess what? The non-Rails endpoints were giving us serialized JSON back in a fraction of the time spent by AMS.
8+
9+
This led us to explore AMS documentation in depth in an effort to try a variety of techniques such as caching, using OJ for JSON string generation etc. It didn’t yield the consistent results we were hoping to get. We loved the developer experience of using AMS, but wanted better performance for our use cases.
10+
11+
We came up with patterns that we can rely upon such as:
12+
13+
* We always use [JSON:API](http://jsonapi.org/) for our APIs
14+
* We almost always serialize a homogenous list of objects (Example: An array of movies)
15+
16+
On the other hand:
17+
18+
* AMS is designed to serialize JSON in several different formats, not just JSON:API
19+
* AMS can also handle lists that are not homogenous
20+
21+
This led us to build our own object serialization library that would be faster because it would be tailored to our requirements. The usage of fast_jsonapi internally on production environments resulted in significant performance gains.
22+
23+
## Benchmark Setup
24+
25+
The benchmark setup is simple with classes for ``` Movie, Actor, MovieType, User ``` on ```movie_context.rb``` for fast_jsonapi serializers and on ```ams_context.rb``` for AMS serializers. We benchmark the serializers with ```1, 25, 250, 1000``` movies, then we output the result. We also ensure that JSON string output is equivalent to ensure neither library is doing excess work compared to the other. Please checkout [object_serializer_performance_spec](https://github.com/Netflix/fast_jsonapi/blob/master/spec/lib/object_serializer_performance_spec.rb).
26+
27+
## Benchmark Results
28+
29+
We benchmarked results for creating a Ruby Hash. This approach removes the effect of chosen JSON string generation engines like OJ, Yajl etc. Benchmarks indicate that fast_jsonapi consistently performs around ```25 times``` faster than AMS in generating a ruby hash.
30+
31+
We applied a similar benchmark on the operation to serialize the objects to a JSON string. This approach helps with ensuring some important criterias, such as:
32+
33+
* OJ is used as the JSON engine for benchmarking both AMS and fast_jsonapi
34+
* The benchmark is easy to understand
35+
* The benchmark helps to improve performance
36+
* The benchmark influences design decisions for the gem
37+
38+
This gem is currently used in several APIs at Netflix and has reduced the response times by more than half on many of these APIs. We truly appreciate the Ruby and Rails communities and wanted to contribute in an effort to help improve the performance of your APIs too.
39+
40+
## Epilogue
41+
42+
[Fast JSON API](https://github.com/Netflix/fast_jsonapi) is not a replacement for AMS. AMS is a great gem, and it does many things and is very flexible. We still use it for non JSON:API serialization and deserialization. What started off as an internal performance exercise evolved into fast_jsonapi and created an opportunity to give something back to the awesome **Ruby and Rails communities**.
43+
44+
We are excited to share it with all of you since we believe that there will be **no** end to this need for speed on APIs. :)

spec/lib/object_serializer_performance_spec.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
}
3434
}
3535

36+
before(:all) { GC.disable }
37+
after(:all) { GC.enable }
38+
3639
context 'when testing performance of serialization' do
3740
it 'should create a hash of 1000 records in less than 50 ms' do
3841
movies = 1000.times.map { |_i| movie }

spec/lib/object_serializer_spec.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@
8989
expect { MovieSerializer.new([movie, movie], options).serializable_hash }.to raise_error(ArgumentError)
9090
end
9191

92+
it 'does not throw an error with non-empty string array includes key' do
93+
options = {}
94+
options[:include] = ['actors']
95+
expect { MovieSerializer.new(movie, options) }.not_to raise_error
96+
end
97+
9298
it 'returns keys when serializing with empty string/nil array includes key' do
9399
options = {}
94100
options[:meta] = { total: 2 }

0 commit comments

Comments
 (0)