This PR adds the ability to create a JSON string directly with Oj::StringWriter, without building any intermediate hashes. Caching is feasible but not implemented.
Performance comparison:
Document Path: /courses/cache_crispies
Document Length: 901053 bytes
Concurrency Level: 1
Time taken for tests: 4.073 seconds
Complete requests: 20
Failed requests: 0
Total transferred: 18030040 bytes
HTML transferred: 18021060 bytes
Requests per second: 4.91 [#/sec] (mean)
Time per request: 203.636 [ms] (mean)
Time per request: 203.636 [ms] (mean, across all concurrent requests)
Transfer rate: 4323.27 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.1 0 0
Processing: 162 203 42.6 193 330
Waiting: 161 202 42.0 192 325
Total: 162 204 42.6 193 330
Percentage of the requests served within a certain time (ms)
50% 193
66% 196
75% 207
80% 208
90% 303
95% 330
98% 330
99% 330
100% 330 (longest request)
Document Path: /courses/cache_crispies_oj
Document Length: 901042 bytes
Concurrency Level: 1
Time taken for tests: 3.436 seconds
Complete requests: 20
Failed requests: 0
Total transferred: 18029820 bytes
HTML transferred: 18020840 bytes
Requests per second: 5.82 [#/sec] (mean)
Time per request: 171.787 [ms] (mean)
Time per request: 171.787 [ms] (mean, across all concurrent requests)
Transfer rate: 5124.72 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 139 172 40.0 157 291
Waiting: 137 170 40.2 156 290
Total: 139 172 40.0 157 291
Percentage of the requests served within a certain time (ms)
50% 157
66% 172
75% 201
80% 211
90% 239
95% 291
98% 291
99% 291
100% 291 (longest request)
All records are cached in Redis to avoid disk IO overhead. The latency reduction is over 15%. Here is a brief list of changes (not easy to break into their own commits though):
A mild refactor of Attribute and HashBuilder, moving reusable code into separate methods.
CacheCrispies::Base.attributes_by_nesting, which groups attributes by their nesting path to ensure attributes with the same nesting level are written together.
CacheCrispies::Base#write_to_json and CacheCrispies::Collection#write_to_json, which serialize the content into a Oj::StringWriter and returns it.
A new class JsonBuilder, which uses serializer.attributes_by_nesting to write JSON structure directly.
Attribute#write_to_json and Attribute#write_serialized_value, which handle the actual writing of values.
An accidental fix for the issue that false is treated equally to nil in child serializers. There are applications in the wild that want to convert false into something like {"enabled": false}.
Unit tests.
Limitation:
show_if -> { false } { nest_in :key { serialize :value } } will render key: {}, because the opening brace has to be emitted before an attribute's condition is evaluated.
When a merged serializer has an attribute with an identical name to another field in the parent serializer, the key will be emitted twice. That is, serialize :key; merge :itself, with: AnotherSerializerWhichSerializesKey results in key: value, key: value.
This PR adds the ability to create a JSON string directly with
Oj::StringWriter
, without building any intermediate hashes. Caching is feasible but not implemented.Performance comparison:
All records are cached in Redis to avoid disk IO overhead. The latency reduction is over 15%. Here is a brief list of changes (not easy to break into their own commits though):
Attribute
andHashBuilder
, moving reusable code into separate methods.CacheCrispies::Base.attributes_by_nesting
, which groups attributes by their nesting path to ensure attributes with the same nesting level are written together.CacheCrispies::Base#write_to_json
andCacheCrispies::Collection#write_to_json
, which serialize the content into aOj::StringWriter
and returns it.JsonBuilder
, which usesserializer.attributes_by_nesting
to write JSON structure directly.Attribute#write_to_json
andAttribute#write_serialized_value
, which handle the actual writing of values.false
is treated equally tonil
in child serializers. There are applications in the wild that want to convertfalse
into something like{"enabled": false}
.Limitation:
show_if -> { false } { nest_in :key { serialize :value } }
will renderkey: {}
, because the opening brace has to be emitted before an attribute's condition is evaluated.serialize :key; merge :itself, with: AnotherSerializerWhichSerializesKey
results inkey: value, key: value
.