আমরা আগে AppSignal Academy-এ রেইলসে ফ্র্যাগমেন্ট ক্যাশিং সম্পর্কে দেখেছিলাম। এটি ভিউয়ের ছোট ছোট টুকরো ক্যাশ করে ভিউর পারফরম্যান্স ব্যাপকভাবে উন্নত করে। পার্শিয়াল ক্যাশ করলে, আমরা সেগুলো আমাদের ভিউতে অন্য জায়গায় পুনরায় ব্যবহার করতে পারি খুব কম খরচে।
এটি ছোট কলেকশনের জন্য ভালো কাজ করে, কিন্তু বড় কলেকশনে দ্রুত সমস্যা দেখা দেয়। এই আর্টিকেলে, আমরা দেখব রেইলস কলেকশন ক্যাশিং কীভাবে কাজ করে এবং কীভাবে আমরা এটি ব্যবহার করতে পারি বড় একটি কলেকশন রেন্ডার করার গতি বাড়াতে।
👋 এবং আপনি যদি এই আর্টিকেল পছন্দ করেন, আমরা রুবি (অন রেইলস) পারফরম্যান্স সম্পর্কে আরও অনেক কিছু লিখেছি, আমাদের রুবি পারফরম্যান্স মনিটরিং চেকলিস্ট দেখুন।
একটি কলেকশন রেন্ডার করা
চলুন একটি ছোট কন্ট্রোলার দিয়ে শুরু করি যা আমাদের ব্লগের ইনডেক্স পেজের জন্য শেষ ১০০টি পোস্ট লোড করে।
class PostsController < ApplicationController
def index
@posts = Post.all.order(:created_at => :desc).limit(100)
end
end
এই পোস্টগুলো ভিউতে রেন্ডার করতে, আমরা @posts ইনস্ট্যান্স ভেরিয়েবলের ওপর লুপ করি।
Posts
<% @posts.each do |post| %>
<%= post.body %>
<% end %>
এই পেজ রিকোয়েস্ট করলে, আমরা দেখি পোস্টগুলো ডেটাবেস থেকে ফেচ হচ্ছে এবং ভিউ রেন্ডার হচ্ছে। ভিউ লেয়ারে শুধু ৩২ মিলিসেকেন্ড খরচ হয়ে, এই পেজ доста দ্রুত।
Started GET "/posts"
Processing by PostsController#index as HTML
Rendering posts/index.html.erb within layouts/application
Post Load (1.5ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ? [["LIMIT", 100]]
↳ app/views/posts/index.html.erb:4
Rendered posts/index.html.erb within layouts/application (19.4ms)
Completed 200 OK in 37ms (Views: 32.4ms | ActiveRecord: 2.7ms)
পার্শিয়াল দিয়ে কলেকশন রেন্ডার করা
পরবর্তী, আমরা post এলিমেন্টটি অন্য একটি ভিউতে ব্যবহার করতে চাই, তাই পোস্ট HTML-কে একটি পার্শিয়ালে সরিয়ে দিই।
Posts
<% @posts.each do |post| %>
<%= render post %>
<% end %>
<%= post.title %>
<%= post.author %>
<%= post.body %>
Started GET "/posts"
Processing by PostsController#index as HTML
Rendering posts/index.html.erb within layouts/application
Post Load (1.2ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ? [["LIMIT", 100]]
↳ app/views/posts/index.html.erb:4
...
Rendered posts/_post.html.erb (0.1ms)
Rendered posts/_post.html.erb (0.1ms)
Rendered posts/index.html.erb within layouts/application (205.4ms)
Completed 200 OK in 217ms (Views: 213.8ms | ActiveRecord: 1.7ms)
ভিউ লেয়ারে ২১৩ মিলিসেকেন্ড খরচ হয়েছে, আপনি দেখতে পাচ্ছেন রেন্ডার টাইম ব্যাপকভাবে বৃদ্ধি পেয়েছে। এটি কারণ প্রতিটি পোস্টের জন্য একটি নতুন ফাইল (পার্শিয়াল) লোড, কম্পাইল এবং রেন্ডার করতে হচ্ছে। চলুন সংক্ষেপে দেখি ফ্র্যাগমেন্ট ক্যাশিং দিয়ে কীভাবে রেন্ডার টাইম উন্নত করা যায়।
ফ্র্যাগমেন্ট ক্যাশিং
ফ্র্যাগমেন্ট ক্যাশিং আর্টিকেলে বর্ণনা অনুযায়ী, আমরা ভিউতে render কলের চারদিক cache হেল্পার ব্যবহার করব। এইভাবে, আমরা প্রতিটি পোস্টের জন্য পার্শিয়াল রেন্ডারিং ক্যাশ করব।
Posts
<% @posts.each do |post| %>
<%= cache post do %>
<%= render post %>
<% end %>
<% end %>
Started GET "/posts"
Processing by PostsController#index as HTML
Rendering posts/index_with_partial_caching.html.erb within layouts/application
Post Load (1.4ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ? [["LIMIT", 100]]
↳ app/views/posts/index.html.erb:4
...
Read fragment views/posts/index.1ms)
Rendered posts/_post.html.erb (0.1ms)
Write fragment views/posts/index.1ms)
Read fragment views/posts/index.5ms)
Rendered posts/_post.html.erb (0.1ms)
Write fragment views/posts/index.1ms)
Rendered posts/index.html.erb within layouts/application (274.5ms)
Completed 200 OK in 286ms (Views: 281.4ms | ActiveRecord: 2.4ms)
প্রথম রিকোয়েস্ট তত দ্রুত হবে না, কারণ এখনও প্রতিটি পার্শিয়াল প্রথমবারের জন্য রেন্ডার করতে হচ্ছে এবং ক্যাশ স্টোরে সংরক্ষণ করতে হচ্ছে।
Started GET "/posts"
Processing by PostsController#index as HTML
Rendering posts/index.html.erb within layouts/application
Post Load (2.2ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ? [["LIMIT", 100]]
↳ app/views/posts/index.html.erb:4
...
Read fragment views/posts/index.1ms)
Read fragment views/posts/index.1ms)
Rendered posts/index.html.erb within layouts/application (63.8ms)
Completed 200 OK in 78ms (Views: 75.5ms | ActiveRecord: 2.2ms)
পরবর্তী রিকোয়েস্টগুলোতে, আমরা দেখি ভিউতে খরচ করা সময় المهمة কমে গেছে - ২৮৬ মিলিসেকেন্ড থেকে ৭৮ মিলিসেকেন্ডে। তবুও, এটি আমাদের মূল কোডের চেয়ে বেশ ধীর - প্রায় দ্বিগুণ ধীর।
নোট: আপনি যদি আপনার লগে "Read/Write fragment" লাইনগুলো না দেখেন, আপনার ডেভেলপমেন্ট এনভায়রনমেন্টে ফ্র্যাগমেন্ট ক্যাশ লগিং ইনিশিয়াল করতে ভুলবেন না, যা রেইলস ৫.১ এবং তার উপরে ডিফল্টভাবে false সেট থাকে:
# config/environments/development.rb
config.action_controller.enable_fragment_cache_logging = true
কলেকশন ক্যাশিং
রেইলস ৫-এ, কলেকশন ক্যাশিং দ্রুত করার জন্য অনেক কাজ করা হয়েছে। এই উন্নতিগুলো ব্যবহার করতে, আমাদের ভিউ কোড পরিবর্তন করতে হবে। নিজেরা cache হেল্পার কল করার বদলে, আমরা রেইলসকে বলতে পারি পুরো কলেকশন রেন্ডার করতে এবং একই সাথে ক্যাশ করতে।
Posts
<%= render partial: :post, collection: @posts, cached: true %>
নোট করুন render @collection, cached: true শর্টহ্যান্ড এই ক্যাশিং স্পিড ইমপ্রুভমেন্টের জন্য কাজ করবে না।
Started GET "/posts"
Processing by PostsController#index as HTML
Rendering posts/index.html.erb within layouts/application
Post Load (1.4ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ? [["LIMIT", 100]]
↳ app/views/posts/index.html.erb:4
Rendered collection of posts/_post.html.erb [0 / 100 cache hits] (28.2ms)
Rendered posts/index.html.erb within layouts/application (46.6ms)
Completed 200 OK in 64ms (Views: 59.9ms | ActiveRecord: 2.0ms)
প্রথম রিকোয়েস্টে, আমরা ভিউ লেয়ারে খরচ করা সময়ে ব্যাপক উন্নতি দেখতে পাচ্ছি। এর কারণ হলো রেইলস এখন পুরো কলেকশনের জন্য ব্যবহৃত পার্শিয়ালটি আগে থেকে প্রস্তুত করে, প্রতিটি পোস্টের জন্য আলাদা করার বদলে।
Started GET "/posts"
Processing by PostsController#index as HTML
Rendering posts/index.html.erb within layouts/application
Post Load (1.3ms) SELECT "posts".* FROM "posts" ORDER BY "posts"."created_at" DESC LIMIT ? [["LIMIT", 100]]
↳ app/views/posts/index.html.erb:4
Rendered collection of posts/_post.html.erb [100 / 100 cache hits] (19.2ms)
Rendered posts/index.html.erb within layouts/application (26.5ms)
Completed 200 OK in 37ms (Views: 35.7ms | ActiveRecord: 1.3ms)
পরবর্তী রিকোয়েস্টগুলোতে, আমরা আরও উন্নতি দেখি - ৬৪ মিলিসেকেন্ড থেকে প্রায় ৩৫ মিলিসেকেন্ডে। রেইলস অপ্টিমাইজেশন দ্বারা পুরো কলেকশনের জন্য একটি বড় স্পিড ইমপ্রুভমেন্ট করা হয়েছে। প্রতিটি পার্শিয়ালের জন্য ক্যাশের উপলব্ধতা চেক করার বদলে, রেইলস একই সাথে কলেকশনের সকল ক্যাশ কী চেক করে, ক্যাশ স্টোর কোয়ারি করার সময় বাঁচায়।
এই ক্যাশিং হেল্পারের একটি এডেড বেনিফিট হলো কলেকশনের সারাংশ লগিং। প্রথম রিকোয়েস্টে, কোনো ক্যাশ কী পাওয়া যায়নি [0 / 100 cache hits], কিন্তু দ্বিতীয় রিকোয়েস্টে, সবই পাওয়া গেল [100 / 100 cache hits]।
ডেটাবেসে কিছু অবজেক্ট আপডেট করার পর, আমরা কতগুলো কী স্টেইল ছিল সেটাও দেখতে পাই।
Rendered collection of posts/_post.html.erb [88 / 100 cache hits] (13.4ms)
এই অপ্টিমাইজড কলেকশন রেন্ডারিং এবং ক্যাশিং দিয়ে অনেক স্পিড ইমপ্রুভমেন্ট লাভ করা যায়। বড় কলেকশন রেন্ডার করার সময় আরও বড় পার্থক্য হবে। যদি আপনার কলেকশনের জন্য কাস্টোমাইজড ভিউ লাগে না, এই অপ্টিমাইজড স্ট্র্যাটেজি আপনার রেইলস অ্যাপসের জন্য পথ। AppSignal-এ, আমরা আমাদের একটি অ্যাডমিন ভিউ যেটি হাজার হাজার রেকর্ড রেন্ডার করত, এই পদ্ধতিতে উল্লেখযোগ্যভাবে স্পিড আপ করলাম।
রেইলসে কলেকশন ক্যাশিং সম্পর্কে কোনো প্রশ্ন আছে? দয়া করে @AppSignal-এ আমাদের জানাতে সংকোচ করবেন না! আপনার যদি আর্টিকেল সম্পর্কে কোনো মন্তব্য থাকে বা আপনার কোনো টপিক থাকে যা আপনি আমাদের কভার করতে চান, তবে আমাদের সাথে যোগাযোগ করুন।