কম্পিউটার

রেলে ভিউ ক্যাশিং সম্পর্কে আপনি যা জানতে চেয়েছিলেন তার সবকিছু

ক্যাশিং হল একটি সাধারণ শব্দ যার অর্থ হল কিছু কোডের ফলাফল সংরক্ষণ করা যাতে আমরা পরে তা দ্রুত পুনরুদ্ধার করতে পারি। এটি আমাদেরকে অনুমতি দেয়, উদাহরণস্বরূপ, ডাটাবেসকে বারবার আঘাত করা এড়াতে এমন ডেটা পেতে যা খুব কমই পরিবর্তিত হয়। যদিও সাধারণ ধারণাটি সব ধরনের ক্যাশিংয়ের জন্য একই, তবে আমরা কী ক্যাশে করার চেষ্টা করছি তার উপর নির্ভর করে রেলগুলি আমাদের বিভিন্ন সহায়তা প্রদান করে৷

রেল ডেভেলপারদের জন্য, ক্যাশিংয়ের সাধারণ ফর্মগুলির মধ্যে রয়েছে মেমোাইজেশন, নিম্ন-স্তরের ক্যাশিং (এই ক্যাশিং সিরিজের পূর্ববর্তী অংশগুলিতে উভয়ই কভার করা হয়েছে), এবং ক্যাশিং দেখুন, যা আমরা এখানে কভার করব।

কিভাবে রুবি অন রেল ভিউ রেন্ডার করে

প্রথমে, আসুন কিছু সামান্য বিভ্রান্তিকর পরিভাষা কভার করি। রেল সম্প্রদায় যাকে "ভিউ" বলে তা হল আপনার app/views-এর ভিতরে থাকা ফাইলগুলি ডিরেক্টরি সাধারণত, এগুলো হল .html.erb ফাইল, যদিও অন্যান্য অপশন আছে (যেমন, প্লেইন .html , .js.erb , অথবা ফাইল যা অন্যান্য প্রিপ্রসেসর ব্যবহার করে, যেমন স্লিম এবং হ্যামল)। অন্যান্য অনেক ওয়েব ফ্রেমওয়ার্কে, এই ফাইলগুলিকে "টেমপ্লেট" বলা হয়, যা তাদের ব্যবহারকে আরও ভালভাবে বর্ণনা করে বলে আমি মনে করি৷

যখন একটি Rails অ্যাপ্লিকেশন একটি GET পায় অনুরোধ, এটি একটি নির্দিষ্ট কন্ট্রোলার অ্যাকশনে পাঠানো হয়, উদাহরণস্বরূপ, UsersController#index . কর্মটি তখন ডাটাবেস থেকে প্রয়োজনীয় তথ্য সংগ্রহ করার জন্য এবং একটি ভিউ/টেমপ্লেট ফাইল রেন্ডার করার জন্য এটিকে পাস করার জন্য দায়ী। এই মুহুর্তে, আমরা "ভিউ লেয়ার" এ প্রবেশ করছি।

সাধারণত, আপনার ভিউ (বা টেমপ্লেট) হবে হার্ড-কোডেড HTML মার্কআপ এবং ডাইনামিক রুবি কোডের মিশ্রণ:

#app/views/users/index.html.erb

<div class='user-list'>
  <% @users.each do |user| %>
    <div class='user-name'><%= user.name %></div>
  <% end %>
</div>

ভিউ রেন্ডার করার জন্য ফাইলে রুবি কোড চালানো দরকার (erb এর জন্য এটা <% %>-এ কিছু ট্যাগ). পৃষ্ঠাটি 100 বার রিফ্রেশ করুন, এবং @users.each... 100 বার মৃত্যুদন্ড কার্যকর করা হবে। যে কোনো আংশিক অন্তর্ভুক্ত জন্য একই সত্য; প্রসেসরকে আংশিক html.erb লোড করতে হবে ফাইল করুন, এর ভিতরে সমস্ত রুবি কোড চালান এবং অনুরোধকারীকে ফেরত পাঠানোর জন্য ফলাফলগুলিকে একটি একক HTML ফাইলে একত্রিত করুন৷

কী কারণে ধীর দর্শন হয়

আপনি সম্ভবত লক্ষ্য করেছেন যে আপনি যখন বিকাশের সময় একটি পৃষ্ঠা দেখেন, তখন রেলগুলি অনেকগুলি লগ তথ্য প্রিন্ট করে, যা নীচের মত কিছু দেখায়:

Processing by PagesController#home as HTML
  Rendering layouts/application.html.erb
  Rendering pages/home.html.erb within layouts/application
  Rendered pages/home.html.erb within layouts/application (Duration: 4.0ms | Allocations: 1169)
  Rendered layouts/application.html.erb (Duration: 35.9ms | Allocations: 8587)
Completed 200 OK in 68ms (Views: 40.0ms | ActiveRecord: 15.7ms | Allocations: 14307)

শেষ লাইনটি এই পর্যায়ে আমাদের জন্য সবচেয়ে দরকারী। বাম থেকে ডানে সময়গুলি অনুসরণ করে, আমরা দেখতে পাই যে ব্রাউজারে একটি প্রতিক্রিয়া ফেরাতে রেলের মোট সময় ছিল 68ms, যার মধ্যে 40ms ব্যয় হয়েছিল erb রেন্ডার করতে। ফাইল এবং 15.7ms ActiveRecord ক্যোয়ারী প্রক্রিয়াকরণে।

যদিও এটি একটি তুচ্ছ উদাহরণ, এটিও দেখায় কেন আমরা ভিউ লেয়ার ক্যাশিং দেখতে চাই। এমনকি যদি আমরা যাদুকরীভাবে ActiveRecord প্রশ্নগুলিকে তাৎক্ষণিকভাবে ঘটাতে পারি, আমরা erb রেন্ডার করতে দ্বিগুণেরও বেশি সময় ব্যয় করছি .

আমাদের ভিউ রেন্ডারিং ধীর হতে পারে এমন কয়েকটি কারণ রয়েছে; উদাহরণ স্বরূপ, আমরা হয়ত ভিউ এর মধ্যে দামী ডিবি কোয়েরি কল করছি বা লুপের মধ্যে অনেক কাজ করছি। আমার দেখা সবচেয়ে সাধারণ পরিস্থিতিগুলির মধ্যে একটি হল অনেকগুলি আংশিক রেন্ডার করা, সম্ভবত একাধিক স্তরের নেস্টিং সহ৷

একটি ইমেল ইনবক্স কল্পনা করুন, যেখানে আমাদের একটি আংশিক থাকতে পারে যা একটি পৃথক সারি পরিচালনা করে:

# app/views/emails/_email.html.erb

<li class="email-line">
  <div class="email-sender">
    <%= email.from_address %>
  </div>
  <div class="email-subject">
    <%= email.subject %>
  </div>
</div>

এবং, আমাদের প্রধান ইনবক্স পৃষ্ঠায়, আমরা প্রতিটি ইমেলের জন্য আংশিক রেন্ডার করি:

# app/views/emails/index.html.erb

...
<% @emails.each do |email| %>
  <%= render email %>
<% end %>

যদি আমাদের ইনবক্সে 100টি বার্তা থাকে, তাহলে আমরা _email.html.erb রেন্ডার করছি আংশিক 100 বার। আমাদের তুচ্ছ উদাহরণ দিয়ে, এটি খুব একটা উদ্বেগের বিষয় নয়। আমার মেশিনে, আংশিকটি পুরো সূচকটি রেন্ডার করতে 15ms লাগে। অবশ্যই, বাস্তব-বিশ্বের উদাহরণগুলি আরও জটিল হবে এবং এমনকি তাদের মধ্যে অন্যান্য আংশিকও থাকতে পারে; রেন্ডারের সময় বাড়ানো কঠিন নয়। এমনকি যদি আমাদের _email রেন্ডার করতে শুধুমাত্র 1-2ms লাগে আংশিক, পুরো সংগ্রহটি করতে 100-200ms সময় লাগবে৷

সৌভাগ্যবশত, রেলের কিছু অন্তর্নির্মিত কার্যকারিতা রয়েছে যা আমাদের সহজেই এই সমস্যাটি সমাধান করতে ক্যাশিং যোগ করতে সাহায্য করে, আমরা কেবল __email ক্যাশে করতে চাই কিনা। আংশিক, index পৃষ্ঠা, বা উভয়।

ভিউ ক্যাশিং কি

Ruby on Rails-এ ভিউ ক্যাশিং এইচটিএমএল গ্রহণ করছে যা একটি ভিউ জেনারেট করে এবং পরে সংরক্ষণ করে। যদিও রেইলস ফাইল সিস্টেমে এগুলি লেখার জন্য বা মেমরিতে রাখার জন্য সমর্থন করে, উত্পাদন ব্যবহারের জন্য, আপনি প্রায় অবশ্যই একটি স্বতন্ত্র ক্যাশিং সার্ভার চাইবেন, যেমন মেমক্যাচেড বা রেডিস। Rails' memory_store ডেভেলপমেন্টের জন্য উপযোগী কিন্তু প্রসেস জুড়ে শেয়ার করা যাবে না (যেমন, একাধিক সার্ভার/ডাইনো বা ফোরকিং সার্ভার, যেমন ইউনিকর্ন)। একইভাবে, file_store সার্ভারে স্থানীয়। অতএব, এটি একাধিক বাক্সে ভাগ করা যাবে না, এবং এটি মেয়াদোত্তীর্ণ এন্ট্রিগুলি স্বয়ংক্রিয়ভাবে মুছে ফেলবে না, তাই আপনাকে পর্যায়ক্রমে Rails.cache.clear কল করতে হবে আপনার সার্ভারের ডিস্ক পূর্ণ হওয়া থেকে আটকাতে।

একটি ক্যাশিং স্টোর সক্ষম করা আপনার পরিবেশ কনফিগারেশন ফাইলে করা যেতে পারে (যেমন, config/environments/production.rb ):

  # memory store is handy for testing
  # during development but not advisable
  # for production
  config.cache_store = :memory_store

একটি ডিফল্ট ইনস্টলেশনে, আপনার development.rb আপনার মেশিনে ক্যাশে সহজে টগল করার অনুমতি দেওয়ার জন্য ইতিমধ্যেই আপনার জন্য কিছু কনফিগারেশন সম্পন্ন করা হবে। সহজভাবে rails dev:cache চালান ক্যাশিং চালু এবং বন্ধ করতে।

রেলে একটি ভিউ ক্যাশ করা প্রতারণামূলকভাবে সহজ, তাই কর্মক্ষমতার পার্থক্য বোঝাতে, আমি শুধু sleep(5) ব্যবহার করব একটি কৃত্রিম বিলম্ব তৈরি করতে:

<% cache do %>
  <div>
    <p>Hi <%= @user.name %>
    <% sleep(5) %>
  </div>
<% end %>

প্রত্যাশিত হিসাবে প্রথমবার এই দৃশ্যটি রেন্ডার করতে 5 সেকেন্ড সময় লাগে৷ যাইহোক, এটিকে দ্বিতীয়বার লোড করতে মাত্র কয়েক মিলিসেকেন্ড সময় লাগে কারণ cache do-এর ভিতরে থাকা সবকিছু ব্লক ক্যাশে থেকে আনা হয়।

উদাহরণ অনুসারে ভিউ ক্যাশিং যোগ করা

আসুন একটি ছোট উদাহরণ দেখা যাক এবং ক্যাশিং এর জন্য আমাদের বিকল্পগুলির মাধ্যমে চলুন। আমরা অনুমান করব যে এই দৃশ্যটি আসলে কিছু কর্মক্ষমতা সমস্যা সৃষ্টি করছে:

# app/views/user/show.html.erb
<div>
  Hi <%= @user.name %>!
<div>

<div>
  Here's your list of posts,
  you've written
  <%= @user.posts.count %> so far
  <% @user.posts.each do |post|
    <div><%= post.body %></div>
  <% end %>
</div>

<% sleep(5) #artificial delay %>

এটি আমাদের কৃত্রিম 5-সেকেন্ড বিলম্বের সাথে কাজ করার জন্য একটি মৌলিক কঙ্কাল দেয়। প্রথমত, আমরা পুরো show.html.erb জুড়ে দিতে পারি একটি cache do ফাইল ব্লক, যেমন আগে বর্ণিত হয়েছে। এখন, একবার ক্যাশে উষ্ণ হলে, আমরা সুন্দর, দ্রুত রেন্ডারিং সময় পাই। যদিও এই প্ল্যানে সমস্যা দেখা শুরু করতে বেশি সময় লাগে না।

প্রথমত, ব্যবহারকারীরা তাদের নাম পরিবর্তন করলে কী হবে? আমাদের ক্যাশে করা পৃষ্ঠার মেয়াদ কখন শেষ হবে তা আমরা রেলকে বলিনি, তাই ব্যবহারকারী কখনই আপডেট হওয়া সংস্করণ দেখতে নাও পারে৷ একটি সহজ সমাধান হল শুধুমাত্র @user পাস করা cache অবজেক্ট পদ্ধতি:

<% cache(@user) do %>
<div>
  Hi <%= @user.name %>!
</div>
...
<% sleep(5) #artificial delay %>
<% end %>

নিম্ন-স্তরের ক্যাশিং সম্পর্কিত এই সিরিজের পূর্ববর্তী নিবন্ধে ক্যাশে কীগুলির বিশদ বিবরণ রয়েছে, তাই আমি এটিকে এখানে আবার কভার করব না। আপাতত, এটা জানা যথেষ্ট যে যদি আমরা একটি মডেলকে cache()-এ পাস করি , এটি সেই মডেলের updated_at ব্যবহার করবে ক্যাশে দেখার জন্য একটি কী তৈরি করতে অ্যাট্রিবিউট। অন্য কথায়, যখনই @user আপডেট করা হয়, এই ক্যাশে করা পৃষ্ঠার মেয়াদ শেষ হয়ে যাবে, এবং রেলগুলি এইচটিএমএলকে পুনরায় রেন্ডার করবে৷

ব্যবহারকারীরা তাদের নাম পরিবর্তন করার সময় আমরা কেসটি যত্ন নিয়েছি, কিন্তু তাদের পোস্টের কী হবে? একটি বিদ্যমান পোস্ট পরিবর্তন করা বা একটি নতুন তৈরি করা updated_at পরিবর্তন করবে না User টাইমস্ট্যাম্প , তাই আমাদের ক্যাশে করা পৃষ্ঠার মেয়াদ শেষ হবে না। উপরন্তু, ব্যবহারকারীরা তাদের নাম পরিবর্তন করলে, আমরা সমস্ত পুনরায় রেন্ডার করব তাদের পোস্টের, এমনকি যদি তাদের পোস্ট পরিবর্তন না হয়। এই উভয় সমস্যা সমাধানের জন্য; আমরা "রাশিয়ান ডল ক্যাশিং" ব্যবহার করতে পারি (অর্থাৎ, ক্যাশে ক্যাশে):

<% cache(@user) do %>
  <div>
    Hi <%= @user.name %>!
  <div>

  <div>
    Here's your list of posts,
    you've written
    <%= @user.posts.count %> so far<br>
    <% @user.posts.each do |post| %>
      <% cache(post) do %>
        <div><%= post.body %></div>
      <% end %>
    <% end %>
  </div>

  <% sleep(5) #artificial delay %>
<% end %>

আমরা এখন প্রতিটি স্বতন্ত্রভাবে রেন্ডার করা post ক্যাশে করছি (বাস্তব জগতে, এটি সম্ভবত একটি আংশিক হবে)। অতএব, @user হলেও আপডেট করা হয়েছে, আমাদের পোস্টটি পুনরায় রেন্ডার করতে হবে না; আমরা শুধু ক্যাশে মান ব্যবহার করতে পারি। যদিও আমরা এখনও একটি সমস্যা আছে. যদি একটি post পরিবর্তিত হয়েছে, আমরা এখনও আপডেট রেন্ডার করব না কারণ @user.update_at পরিবর্তিত হয়নি, তাই cache(@user) do-এর ভিতরে ব্লক চালানো হবে না।

এই সমস্যাটি সমাধান করতে, আমাদের touch: true যোগ করতে হবে আমাদের post-এ মডেল:

class Post < ApplicationRecord
  belongs_to :user, touch: true
end

touch: true যোগ করে এখানে, আমরা ActiveRecord কে বলছি যে প্রতিবারই একটি পোস্ট আপডেট করা হয়েছে, আমরা updated_at চাই ব্যবহারকারীর টাইমস্ট্যাম্প এটি "এর অন্তর্গত" এছাড়াও আপডেট করা হবে৷

আমার এটাও যোগ করা উচিত যে রেলগুলি আংশিক সংগ্রহের রেন্ডার করার জন্য একটি নির্দিষ্ট সহায়ক প্রদান করে, এটি কতটা সাধারণ তা বিবেচনা করে:

  <%= render partial: 'posts/post',
       collection: @posts, cached: true %>

যা কার্যকরীভাবে নিম্নলিখিতগুলির সমতুল্য:

<% @posts.each do |post| %>
  <% cache(post) do %>
    <%= render post %>
  <% end %>
<% end %>

শুধুমাত্র render partial: ... cached: true নয় কম ভার্বোস আকারে, এটি আপনাকে কিছু অতিরিক্ত দক্ষতাও দেয় কারণ রেলগুলি সংগ্রহের প্রতিটি আইটেমের জন্য আপনার ক্যাশে স্টোরে আঘাত করার পরিবর্তে ক্যাশে স্টোরে একটি মাল্টিগেট ইস্যু করতে পারে (অর্থাৎ, একটি একক রাউন্ড-ট্রিপে অনেক কী/মূল্য জোড়া পড়া)।

ডাইনামিক পৃষ্ঠা সামগ্রী

কিছু পৃষ্ঠায় কিছু পরিমাণ 'গতিশীল' বিষয়বস্তু অন্তর্ভুক্ত করা সাধারণ যেটি তার চারপাশের বাকি পৃষ্ঠাগুলির তুলনায় অনেক দ্রুত হারে পরিবর্তিত হয়। এটি বিশেষ করে হোম পেজ বা ড্যাশবোর্ডে সত্য, যেখানে আপনার কার্যকলাপ/সংবাদ ফিড থাকতে পারে। আমাদের ক্যাশে করা পৃষ্ঠায় এগুলি অন্তর্ভুক্ত করার অর্থ হতে পারে যে আমাদের ক্যাশে ঘন ঘন অকার্যকর করা প্রয়োজন, যা প্রথম স্থানে ক্যাশে করার ফলে আমরা যে সুবিধা পাই তা সীমিত করে৷

একটি সাধারণ উদাহরণ হিসাবে, আসুন আমাদের দর্শনে বর্তমান দিন যোগ করি:

<% cache(@user) do %>
  <div>
    Hi <%= @user.name %>,
    hope you're having a great
    <%= Date.today.strftime("%A") %>!
  <div>

  ...
<% end %>

আমরা প্রতিদিন ক্যাশে বাতিল করতে পারি, কিন্তু সুস্পষ্ট কারণে এটি খুব বাস্তব নয়। একটি বিকল্প হল একটি স্থানধারক মান ব্যবহার করা (বা এমনকি একটি খালি <span> ) এবং জাভাস্ক্রিপ্ট দিয়ে এটি পপুলেট করুন। এই ধরনের পদ্ধতিকে প্রায়শই "জাভাস্ক্রিপ্ট স্প্রিঙ্কলস" বলা হয় এবং এটি মূলত বেসক্যাম্প দ্বারা পছন্দ করা একটি পদ্ধতি, যেখানে প্রচুর রেলের মূল কোড তৈরি করা হয়। ফলাফল এরকম কিছু হবে:

<% cache(@user) do %>
  <div>
    Hi <%= @user.name %>,
    hope you're having a great
    <span id='greeting-day-name'>Day</span>!
  <div>

  ...
<% end %>

<script>
 // assuming you're using vanilla JS with turbolinks
 document.addEventListener(
   "turbolinks:load", function() {
   weekdays = new Array('Sunday', 'Monday',
     'Tuesday', 'Wednesday', 'Thursday',
     'Friday', 'Saturday');
     today = weekdays[new Date().getDay()];
   document.getElementById("greeting-day-name").textContent=today;
 });
</script>

আরেকটি পদ্ধতি হল দৃশ্যের শুধুমাত্র কিছু অংশ ক্যাশে করা। আমাদের উদাহরণে, অভিবাদনটি পৃষ্ঠার শীর্ষে রয়েছে, তাই এটি শুধুমাত্র নিম্নলিখিতগুলি ক্যাশে করা মোটামুটি তুচ্ছ:

<div>
  Hi <%= @user.name %>,
  hope you're having a great
  <%= Date.today.strftime("%A") %>!
<div>

<% cache(@user) do %>
  ...
<% end %>

স্পষ্টতই, বাস্তব জগতে আপনি যে লেআউটগুলি খুঁজে পান তার সাথে এটি প্রায়শই সহজ নয়, তাই আপনি কোথায় এবং কীভাবে ক্যাশিং প্রয়োগ করবেন সে সম্পর্কে আপনাকে বিবেচনা করতে হবে।

সতর্কতার শব্দ

কর্মক্ষমতা সমস্যাগুলির জন্য একটি দ্রুত এবং সহজ প্যানেসিয়া হিসাবে ভিউ ক্যাশিংকে দেখা সহজ। প্রকৃতপক্ষে, রেল এটিকে অবিশ্বাস্যভাবে করে ভিউ এবং আংশিক ক্যাশে করা সহজ, এমনকি যখন তারা গভীরভাবে নেস্টেড থাকে। এই সিরিজের প্রথম প্রবন্ধে, আমি আপনার সিস্টেমে ক্যাশিং যোগ করার সময় যে সমস্যাগুলি দেখা দিতে পারে তা তুলে ধরেছি, কিন্তু আমি মনে করি এটি ভিউ-লেভেল ক্যাশিংয়ের ক্ষেত্রে বিশেষভাবে সত্য৷

কারণটি হল যে দৃষ্টিভঙ্গি, তাদের প্রকৃতির দ্বারা, একটি সিস্টেমের অন্তর্নিহিত ডেটার সাথে আরও বেশি মিথস্ক্রিয়া করে। আপনি যখন Rails-এ মেমোাইজেশন বা নিম্ন-স্তরের ক্যাশিং প্রয়োগ করেন, তখন ক্যাশে করা মানটি কখন এবং কেন রিফ্রেশ করা উচিত তা নির্ধারণ করতে আপনাকে প্রায়শই আপনার ফাইলের বাইরে দেখার প্রয়োজন হয় না। অন্যদিকে, একটি দৃশ্যের একাধিক ভিন্ন মডেল থাকতে পারে, এবং ইচ্ছাকৃত পরিকল্পনা ছাড়া, কোন মডেলের কারণে কোন দৃশ্যের কোন অংশটি কোন সময়ে পুনরায় উপস্থাপন করা হবে তা দেখা কঠিন হতে পারে।

নিম্ন-স্তরের ক্যাশিংয়ের মতো, সর্বোত্তম পরামর্শ হল আপনি কোথায় এবং কখন এটি ব্যবহার করবেন সে সম্পর্কে কৌশলগত হওয়া। পারফরম্যান্সের একটি গ্রহণযোগ্য স্তর অর্জন করতে আপনি যতটা সম্ভব কম ক্যাশিং ব্যবহার করুন, যতটা সম্ভব কম জায়গায়।

ডিফল্টরূপে রেলের ক্যাশিং

ক্যাশিং-এর এই সিরিজে এখনও পর্যন্ত আমরা ম্যানুয়ালি জিনিসগুলি ক্যাশ করার উপায়গুলি কভার করেছি কিন্তু এমনকি কোনও ম্যানুয়াল কনফিগারেশন ছাড়াই, ActiveRecord ইতিমধ্যেই কিছু ক্যাশিং করে কোয়েরির গতি বাড়ানোর জন্য (বা সম্পূর্ণরূপে এড়িয়ে যান)। ক্যাশিং-এর এই সিরিজের পরবর্তী নিবন্ধে আমরা দেখব যে ActiveRecord আমাদের জন্য কী ক্যাশে করছে এবং কীভাবে অল্প পরিমানে কাজ করে আমরা এটিকে একটি "কাউন্টার ক্যাশে" রাখতে পারি যাতে thing.children.size আপ টু ডেট গণনা পেতে ডাটাবেসকে মোটেও আঘাত করতে হবে না।


  1. আপনার যা কিছু জানা দরকার:GDPR

  2. Samsung Galaxy S9:এটি সম্পর্কে আপনার যা কিছু জানা উচিত

  3. ব্লুটুথ 5 সম্পর্কে আপনার যা কিছু জানা দরকার

  4. PBM ফাইল সম্পর্কে আপনার যা কিছু জানা দরকার?