problems and solution of caching on view layer
DESCRIPTION
When using caching system on view layer, there are problems around context data which are required only when updating cache. For example, a lot of useless DB access, MVC violation, and so on. In this presentation, I describes these problems and proposes simple but useful solution.TRANSCRIPT
copyright(c) 2009 kuwata-lab.com all rights reserved.
Problems and Solution of Caching on View Layer
makoto kuwata <[email protected]>
http://www.kuwata-lab.com/
2009 Sapporo RubyKaigi
1
copyright(c) 2009 kuwata-lab.com all rights reserved.
Caching Classification (Rails)
Page cacheCache whole page and use it as static HTML pageAction cacheSimilar to Page cache but you can controll it by filter (ex. authentication)Fragment cacheCache some parts of HTML page
Today's topic
2
copyright(c) 2009 kuwata-lab.com all rights reserved.
Fragment Cache
Cache some parts of HTML page generated on view later and reuse it
<% cache do %> <ul> <% @books.each do ¦book¦ %> <li><%= book.title %></li> <% end %> </ul><% end %>
View: You can controll caches more finely than page or action cache
3
copyright(c) 2009 kuwata-lab.com all rights reserved.
Problem (1)
Prepare context data everytime which is necessary only when cache is renewed.=> A lot of useless access to DB
def index ## @books is necessary only when ## updating cache, but prepared everytime @books = Book.find(:all, :limit=>5) renderend
View:
4
copyright(c) 2009 kuwata-lab.com all rights reserved.
Problem (2)
Direct access to Model from view layter=> MVC Violation
View:<% cache do %><% @books = Book.find(:all,:limit=>5) %> <ul> <% @books.each do ¦book¦ %> <li><%=h book.title %></li> <% end %> </ul><% end %>
5
copyright(c) 2009 kuwata-lab.com all rights reserved.
Substance of the problem
View
ControllerModel
What we need: pullOnly context data
requred in template are pulled from C or M
What we use: pushcontext data are prepared in C and pushed into template
6
copyright(c) 2009 kuwata-lab.com all rights reserved.
Solution
Closure (Proc) as callback objectUse closure as lazy evaluation
7
copyright(c) 2009 kuwata-lab.com all rights reserved.
Example: Controller
def index @user = session[:user] # create callback object @books = proc { Book.find(:all, :limit=>5) } renderend
Controller:
At this point, DB is not accessed yet
8
copyright(c) 2009 kuwata-lab.com all rights reserved.
Example: View
<% cache do %> <ul> <%# invoke calback object %> <%# oly when updating cache %> <% @books.call.each do ¦book¦ %> <li><%=h book.title %></li> <% end %> </ul><% end %>
View:
DB is accessed at this time
9
copyright(c) 2009 kuwata-lab.com all rights reserved.
Pros
Prepare context data only when requiredReduce useless DB accessNo need to access DB directly from view layerKeep MVCAble to mix 'push' and 'pull''Push' is for non-cache, 'pull' is for cacheNo need to change program entirely
10
copyright(c) 2009 kuwata-lab.com all rights reserved.
merb_piece_cache
Merb用 fragment cache plug-inhttp://github.com/kwatch/merb_piece_cache
def show(id) @id = id if_not_cached {¦cache_key¦ @books = Book.all(:limit=>5) } renderend
Controller:
evaluated only when updating cache
Possible to change behavior according to cache key
11
copyright(c) 2009 kuwata-lab.com all rights reserved.
merb_piece_cache
<% cache_with("books", 5*60) do %> <ul> <% @books.each do ¦book¦ %> <li><%=h book.title %></li> <% end %> </ul><% end %>
View:Cache key Cache lifetime (sec)
No more '.call'
12
copyright(c) 2009 kuwata-lab.com all rights reserved.
Conclusion
Two types for passing context data'push': prepare all data in advance'pull': prepare data only when required'Pull' is a friend of cacheReduce useless DB accessKeep MVCEasy to implement 'pull' with closure
13
thank you
14