-
Notifications
You must be signed in to change notification settings - Fork 440
Caching is working but slower #259
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
Comments
The issue is that jbuilder caching is fairly naive - it basically dumps a serialized version of a giant activerecord blob into the cache store, then pulls it out, deserializes it, and then EVENTUALLY serializes THAT to JSON. In the future, jbuilder will hopefully act directly on strings, but until then, I think jbuilder caching is best not used russian-doll style. |
Well, @vincentwoo is right. Currently caching provides benefits only when it allows to skip some of AR queries or you're using really computionally heavy view helpers. When you already have the records fetched and only perform basic extractions, caching only slows things down. |
Can anyone confirm that this behaviour also exist on RABL ? |
@artmees: I think that question is best asked to the RABL user group or github issues list. @rwz: Any chance you could document the caching tradeoffs of jbuilder in the README? Have definitely seen this issue trip up a few developers. They expected that jbuilder have basically the same tradeoffs that normal erb view caching does, and it's surprising to find out that it does not. |
BTW if anyone of you both @rwz and @vincentwoo wants to add the answer to the stackoverflow question and claim the bounty... be my guest... +1 for adding the documentation |
Cool, I posted a long answer on SO: http://stackoverflow.com/questions/29190745/jbuilder-rails-caching-is-slower/29358844#29358844 |
Any thoughts on what would be the best approach to cache the JSON itself? I'm happy to put in a PR, but don't see a clear path forward. Right now the entire Jbuilder is serialized to JSON at once with It would be ideal if |
anyone working on this issue or any fork addressing this? |
Not that I'm aware of. I keep this issue open to remind me to update README. |
@xinuc Due to the current way jbuilder works it would likely require large changes (and possibly API changes) which is a problem due to the large adoption of jbuilder. I think the best path forward would be creating a new gem focused on better caching. Feel free to reach out if you are interested in collaborating. |
@droberts84 @xinuc I think the main problem is not Jbuilder, but the total absence of partial caching API for JSON. |
@rzw What do you think about my approach mentioned above, and caching the JSON as an object in the cache store? I would love to get this working with Jbuilder, and am happy to contribute. However, I currently have a use case which requires very large JSON structures that are often the same, so for best performance I need to create some sort of workaround for this issue. |
As far as I can understand, the underlying problem is the lack of mean to append a json string to the serializer. Please CMIIW. a stupid hack I can think of is to parse the string and merge it again as a hash json.something JSON.parse('{"some": "json string"}') is there any better way we can achieve this? |
well, some quick searches, and apparently appending arbitrary string is not possible. so I think parsing the json and merging it again is not that stupid after all 😓 |
Parsing JSON string and merging it doesn't make too much sense because it's even slower than existing caching workflow we have right now. |
yeah, I'd be surprised if it's not. |
I think @rwz is right: the main reason is partial. |
I agree with @smileyan I experienced the same performance benefits by abandoning that approach. |
Would it make sense to swap out the cache system in favor of
jbuilder cache:
No cache:
Collection cache:
Here's an example project for how to get this to work. Run it and then you can visit:
#!/bin/bash
set -eux
gem install rails
rails new blog \
--skip-action-cable \
--skip-action-mailbox \
--skip-action-mailer \
--skip-action-text \
--skip-active-record \
--skip-active-storage \
--skip-bootsnap \
--skip-git \
--skip-javascript \
--skip-keeps \
--skip-listen \
--skip-puma \
--skip-spring \
--skip-sprockets \
--skip-system-test \
--skip-test \
--skip-turbolinks \
--skip-webpack-install
mkdir -p blog/app/views/users
cat > blog/config/routes.rb <<- EOF
Rails.application.routes.draw do
resources :users do
collection do
get :jbuilder_cache
get :collection_cache
get :no_cache
end
end
end
EOF
cat > blog/app/views/users/collection_cache.html.erb <<- EOF
[<%= render partial: "users/user", collection: @users, as: :user, spacer_template: "comma", cached: true %>]
EOF
cat > blog/app/views/users/_comma.html.erb <<- EOF
,
EOF
cat > blog/app/views/users/no_cache.json.jbuilder <<- EOF
json.array!(@users) do |user|
json.partial! "users/user", user: user
end
EOF
cat > blog/app/views/users/jbuilder_cache.json.jbuilder <<- EOF
json.array!(@users) do |user|
json.partial! "users/cached_user", user: user
end
EOF
cat > blog/app/views/users/_cached_user.json.jbuilder <<- EOF
json.cache! user do
json.partial! "users/user", user: user
end
EOF
cat > blog/app/controllers/users_controller.rb <<- EOF
class UsersController < ApplicationController
before_action :users
def collection_cache
render template: "users/collection_cache.html.erb", layout: nil, content_type: "application/json"
end
def jbuilder_cache
end
def no_cache
end
private
def users
@users = 100.times.map.with_index { |index| user(index) }
end
def user(index)
OpenStruct.new({
cache_key: index.to_s,
cache_version: Time.parse("2020-04-19T11:37:11+02:00"),
content: "<p>This is <i>serious</i> monkey business</p>",
visitors: 15,
author: OpenStruct.new({
name: "David H.",
email_address: "'David Heinemeier Hansson' <[email protected]>",
url: "http://example.com/users/1-david.json"
}),
comments: [
OpenStruct.new({ content: "Hello everyone!", created_at: Time.parse("2011-10-29T20:45:28-05:00") }),
OpenStruct.new({ content: "To you my good sir!", created_at: Time.parse("2011-10-29T20:47:28-05:00") })
],
attachments: [
OpenStruct.new({ filename: "forecast.xls", url: "http://example.com/downloads/forecast.xls" }),
OpenStruct.new({ filename: "presentation.pdf", url: "http://example.com/downloads/presentation.pdf" })
]
})
end
end
EOF
cat > blog/app/views/users/_user.json.jbuilder <<- EOF
json.content user.content
json.(user, :created_at, :updated_at)
json.author do
json.name user.author.name
json.email_address user.author.email_address
json.url user.author.url
end
json.visitors user.visitors
json.comments user.comments, :content, :created_at
json.attachments user.attachments do |attachment|
json.filename attachment.filename
json.url attachment.url
end
EOF
touch blog/tmp/caching-dev.txt
blog/bin/rails server |
@benubois I tried a similar approach a while back from what I recall there were numerous cases where the template didn't contain enough information about where the cache was being inserted to know how to insert it and give valid JSON. I followed up on #289 here; TLDR: tried, failed, ended up making TurboStreamer |
Just reread your comment @benubois and this may work if you only cache partials that are always make up an array |
I've tried to use caching with collections (with multiple solutions) the problem is that when ever I try caching the response become slower
consider the following example of a collection that renders 2 partials for every item in it (around 25 item)
without caching the average response time is around ~38ms (on average)
now with caching
with the jbuilder default caching and dalli store is properly installed and configured (I could verify that there was no cache miss)
the average response is around ~59ms (on average)
using the syntax found on Cache Digest
the average response time is ~41ms (on average), and the response is different than the other responses
but the cache digest of the file is a very big string that will easily exceed the unix max file name length.
this is the filename for example.
I've also tired Jbuilder Cache Multi
and the response was around ~57ms (on average)
plus with both jbuilder cache and multi I'm getting a lot of these in the logs
so is it something wrong with my implementation or machine or local environment ?
Rails 4.2.0, and Jbuilder 2.2.11
I also posted this issue to StackOverflow
The text was updated successfully, but these errors were encountered: