এই পোস্টে, আমরা রেলস ভিউ পারফরম্যান্স উন্নত করার যাচাইকৃত পদ্ধতিগুলোতে নজর দেব। নির্দিষ্টভাবে, আমি ডাটাবেস দক্ষতা, ভিউ ম্যানিপুলেশন, এবং ক্যাশিং-এ ফোকাস করব।
মনে হয় "premature optimization is the root of all evil" উক্তিটি একটু আউট অফ কনটেক্সট নেওয়া হয়েছে। আমি часто ডেভেলপাররা কোড রিভিউতে এই কথা বলতে শুনি যখন সিম্পল অপ্টিমাইজেশন টেকনিক গোঁড়া করা হয়। আপনি সেই বিখ্যাত কথা জানেন, "আমি আগে কাজ করে ফেলব, তারপর অপ্টিমাইজ করব" - তারপর টেস্ট করব - তারপর ডিবাগ করব - আবার টেস্ট করব, এবং এভাবে চলতে থাকে!
ধন্যবাদ, কিছু সিম্পল এবং কার্যকর পারফরম্যান্স ও অপ্টিমাইজেশন টেকনিক আছে যেগুলো আপনি কোড লিখা শুরু করার মুহূর্ত থেকে ব্যবহার করতে পারেন।
👋 আপনি যদি এই আর্টিকেল পছন্দ করেন, আমাদের Ruby performance monitoring checklist-এ অন্যান্য Ruby (on Rails) পারফরম্যান্স আর্টিকেল দেখুন।
পোস্টটি আমরা একটি বেসিক রেলস অ্যাপ ধরে করব, সেটিতে উন্নতি আনব এবং রেজাল্ট কমপেয়ার করব।
বেসিক রেলস অ্যাপের নিম্নলিখিত মডেলগুলো আছে:
Person (has many addresses)
- name:string
- votes_count:integer
Profile (belongs to Person)
- address:string
এখানে আমাদের Person মডেলের কোড:
# == Schema Information # # Table name: people # # id :integer not null, primary key # name :string # votes_count :integer # created_at :datetime not null # updated_at :datetime not null # class Person < ApplicationRecord # Relationships has_many :profiles # Validations validates_presence_of :name validates_uniqueness_of :name def vote! update votes_count: votes_count + 1 end end
এখানে আমাদের Profile মডেলের কোড:
# == Schema Information # # Table name: profiles # # id :integer not null, primary key # address :text # person_id :integer # created_at :datetime not null # updated_at :datetime not null # class Profile < ApplicationRecord # Relationships belongs_to :person # Validations validates_presence_of :address end
এছাড়া একটি seed ফাইল আছে ১০০০ person পপুলেট করার জন্য। আমরা Faker জেম ব্যবহার করে সহজেই এটি করতে পারি।
আমরা এখন ApplicationController-এ "home" নামে একটি অ্যাকশন তৈরি করব।
def home @people = Person.all end
আমাদের home.html.erb এর কোড নিম্নরূপ:
<ul> <% @people.each do |person| %> <li id="<%= person.id %>"><%= render person %></li> <% end %> </ul>
আমরা এখন একটি ড্রাই রান করব এবং এই পেজের পারফরম্যান্স মাপব।
এই পেজ লোড হয়েছে সম্পূর্ণ ১০৬৬.৭ms-এ। ভালো নয়! এটি আমরা কমানোর লক্ষ্য রাখব।
ডাটাবেস কুয়েরি
একটি পারফরম্যান্ট অ্যাপ্লিকেশন বানানোর প্রথম ধাপ হল রিসোর্স ইউটিলাইজেশন maksimize করা। অধিকাংশ রেলস অ্যাপ ডাটাবেস থেকে কিছু রেন্ডার করে ভিউতে, তাই ডাটাবেস কল অপ্টিমাইজ করার চেষ্টা করি! এই ডেমো için আমি MySQL ডাটাবেস ব্যবহার করব।
দেখি সেটি ইনিশিয়াল ১০৬৬ms লোড কিভাবে ব্রেক ডাউন হয়েছে।
৪১৪.৭ms 'controllers/application_controller#home' এক্সিকিউট করতে
... (0.1ms) SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ? [["person_id", 996]] Rendered people/_person.html.erb (1.5ms) (0.2ms) SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ? [["person_id", 997]] Rendered people/_person.html.erb (2.3ms) (0.1ms) SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ? [["person_id", 998]] Rendered people/_person.html.erb (2.1ms) (0.2ms) SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ? [["person_id", 999]] Rendered people/_person.html.erb (2.3ms) (0.2ms) SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ? [["person_id", 1000]] Rendered people/_person.html.erb (2.0ms) Rendered application/home.html.erb within layouts/application (890.5ms) Completed 200 OK in 1066ms (Views: 890.5ms | ActiveRecord: 175.4ms)
৫১৯.২ এবং ১৩২.৮ms "application/home.html.erb" এবং "people/_person.html.erb" পারশিয়াল রেন্ডার করতে।
আপনি কি অদ্ভুত কিছু লক্ষ্য করেছেন?
কন্ট্রোলারে আমরা একটি ডাটাবেস কল করলাম, কিন্তু প্রতিটি পারশিয়ালও নিজস্ব ডাটাবেস কল করছে! এখানে পরিচিত, N+1 কুয়েরি সমস্যা।
১. N+1 কুয়েরি
এটি খুব জনপ্রিয় এবং সহজ অপ্টিমাইজেশন টেকনিক—তবে এটি প্রথমে উল্লেখ করার যোগ্য কারণ এই ভুলটা খুব প্রচলিত।
দेखি "people/_person.html.erb" কী করে:
<ul> <li> Name: <%= person.name %> </li> <li> Addresses: <ul> <% person.profiles.each do |profile| %> <li><%= profile.address %></li> <% end %> </ul> </li> </ul> <%= button_to "Vote #{person.votes_count}", vote_person_path(person) %>
বাসিক্যালি, এটি সেই পেরসনের প্রোফাইলের জন্য ডাটাবেস কুয়েরি করে এবং প্রতিটা রেন্ডার করে। তাই এটি N কুয়েরি করে (যেখানে N হল পেরসনের সংখ্যা) এবং ১টি কুয়েরি আমরা কন্ট্রোলারে করলাম—সুতরাং, N+1।
এটি অপ্টিমাইজ করতে, MySQL ডাটাবেস জয়েন এবং রেলস ActiveRecord includes ফাংশন ব্যবহার করুন।
কন্ট্রোলারকে নিম্নরূপ পরিবর্তন করুন:
def home @people = Person.all.includes(:profiles) end
সব পেরসন ১টি MySQL কুয়েরিতে লোড হয়, এবং তাদের সব প্রোফাইল আরেকটি কুয়েরিতে লোড হয়। N+1 কে শুধু ২ কুয়েরিতে নামিয়ে আনল।
দেখি কতটা পারফরম্যান্স বাড়েছে!
পেজ লোড হয়েছে মাত্র ৯৩৬ms-এ। নিচে দেখুন "application_controller#home" অ্যাকশন ২টি MySQL কুয়েরি করছে।
Rendered people/_person.html.erb (0.3ms) Rendered people/_person.html.erb (0.2ms) Rendered people/_person.html.erb (0.3ms) Rendered people/_person.html.erb (0.3ms) Rendered people/_person.html.erb (0.3ms) Rendered people/_person.html.erb (0.3ms) Rendered people/_person.html.erb (0.3ms) Rendered people/_person.html.erb (0.2ms) Rendered application/home.html.erb within layouts/application (936.0ms) Completed 200 OK in 936ms (Views: 927.1ms | ActiveRecord: 9.3ms)
২. শুধু যা ব্যবহার করবেন সেটি লোড করুন
হোমপেজটি কেমন দেখায়। আপনি দেখতে পাচ্ছেন আমাদের শুধু address দরকার, আর কিছু নেই। কিন্তু "_person.html.erb" পারশিয়ালে আমরা পুরো প্রোফাইল অবজেক্ট লোড করছি। দেখি কিভাবে এটা পরিবর্তন করা যায়।
<li> Addresses: <ul> <% person.profiles.pluck(:address).each do |address| %> <li><%= address %></li> <% end %> </ul> </li>
N+1 কুয়েরি সম্পর্কে আরও গভীরে জানতে, "ActiveRecord performance: the N+1 queries antipattern" পড়ুন।
প্রো টিপ: আপনি এর জন্য একটি স্কোপ তৈরি করে "models/profile.rb" ফাইলে যোগ করতে পারেন। ভিউ ফাইলে র AW ডাটাবেস কুয়েরি খুব কাজের নয়।
৩. সব ডাটাবেস কল কন্ট্রোলারে সরান
ধরি, এই কাল্পনিক অ্যাপ্লিকেশনের ভবিষ্যতে, আপনি হোম পেজে মোট ইউজারের সংখ্যা দেখাতে চান। সহজ! ভিউতে একটি কল করুন এমনভাবে:
# of People: <%= @people.count %>
ঠিক আছে, এটাও সহজ। আর একটি রিকোয়ার্মেন্ট—আপনাকে একটি UI এলিমেন্ট তৈরি করতে হবে যা পেজ প্রগ্রেস দেখাবে। এখন পেজের পেরসন সংখ্যাকে টোটাল কাউন্ট দিয়ে ভাগ করুন।
Progress: <%= index / @people.count %>
দুর্ভাগ্যবশত, আপনার কলিগ না জানে আপনি ইতিমধ্যে এই কুয়েরি করেছেন এবং সে ভিউতে আবার আবার সেটি করে। যদি আপনার কন্ট্রোলার এরকম হতো:
def home @people = Person.all.includes(:profiles) @people_count = @people.count end
তবে ইতিমধ্যে ক্যালকুলেট করা ভেরিয়েবল রিয়ুজ করা সহজ হতো। যদিও এটি সরাসরি পেজ লোড স্পিড বাড়ায় না, তবে ভিন্ন ভিউ পেজ থেকে ডাটাবেসে একাধিক কল প্রতিরোধ করে এবং পরে ক্যাশিংের মত অপ্টিমাইজেশনের জন্য প্রস্তুত করে।
৪. যেখানো সম্ভব পেজিনেশন করুন!
শুধু যা দরকার সেটি লোড করার মত, শুধু যা দরকার সেটি দেখাওতে সাহায্য করে! পেজিনেশনের সাথে, ভিউ তথ্যের একটি অংশ রেন্ডার করে এবং বাকি ডিমান্ড অনুযায়ী লোড করে। এতে অনেক মিলিসেকেন্ড বাঁচায়! will_paginate এবং kaminari জেমগুলো এটি মিনিটে করে দেয়।
এটি একটু বিরক্তি সৃষ্টি করে কারণ ইউজারদের বারবার "Next Page"-এ ক্লিক করতে হয়। সেজন্য, "Infinite Scrolling"-এও নজর দিতে পারেন ইউজার এক্সপেরিয়েন্স আরও ভালো করার জন্য।
HTML রিলোড এড়ানো
ট্রেডিশনাল রেলস অ্যাপে, HTML ভিউ রেন্ডারিং অনেক সময় নেয়। সৌভাগ্যবশত, এটি কমানোর কিছু উপায় আছে।
১. Turbolinks
এটি আপনার স্ট্যান্ডার্ড রেলস অ্যাপের সাথে বান্ডেলড থাকে। Turbolinks একটি জাভাস্ক্রিপ্ট লাইব্রেরি যা সব জায়গায় কাজ করে (রেলস ছাড়া også, যেমন স্ট্যাটিক পেজে) এবং অনুপযোগী ব্রাউজারে gracefully degrade করে। এটি প্রতিটি লিংককে AJAX রিকোয়েস্টে কনভার্ট করে এবং পুরো বডি JS দিয়ে রিপ্লেস করে। এটি পারফরম্যান্স খুব উন্নত করে কারণ CSS, JS এবং ইমেজ রিলোড করতে হয় না। তবে, কাস্টম JS লিখার সময় আপনাকে "Turbolinks safe JS" লিখতে অতিরিক্ত সতর্কতা অবলম্বন করতে হবে। এ সম্পর্কে আরো পড়ুন।
২. AJAX রিকোয়েস্ট ব্যবহার করুন
Turbolinks-এর মতই, আপনার কিছু লিংক এবং বাটন AJAX রিকোয়েস্টে কনভার্ট করতে পারেন। পার্থক্য হল আপনি কন্ট্রোল করতে পারেন কোন HTML রিপ্লেস হবে, পুরো বডি রিপ্লেস করার বদলে যেমন Turbolinks করে। AJAX একশনে দেখি! স্যাম্পল অ্যাপে প্রতিটি ইউজারের জন্য একটি "Vote" বাটন আছে। তা কতক্ষণ লাগে তা মাপি।
Started POST "/people/1/vote" for 127.0.0.1 at 2020-01-21 14:50:49 +0530 Processing by PeopleController#vote as HTML Person Load (0.3ms) SELECT "people".* FROM "people" WHERE "people"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] (0.1ms) begin transaction Person Exists (4.5ms) SELECT 1 AS one FROM "people" WHERE "people"."name" = ? AND ("people"."id" != ?) LIMIT ? [["name", "Deon Waelchi"], ["id", 1], ["LIMIT", 1]] SQL (1.0ms) UPDATE "people" SET "votes_count" = ?, "updated_at" = ? WHERE "people"."id" = ? [["votes_count", 1], ["updated_at", "2020-01-21 09:20:49.941928"], ["id", 1]] Redirected to https://localhost:3000/ Completed 302 Found in 24ms (ActiveRecord: 7.5ms) Started GET "/" for 127.0.0.1 at 2020-01-21 14:50:49 +0530 Processing by ApplicationController#home as HTML Rendering application/home.html.erb within layouts/application Rendered people/_person.html.erb (2.4ms) (0.3ms) SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ? [["person_id", 30]] Rendered people/_person.html.erb (2.2ms) ... Rendered application/home.html.erb within layouts/application (159.8ms) Completed 200 OK in 190ms (Views: 179.0ms | ActiveRecord: 6.8ms)
এটি পেজ রিলোডের সমান সময় নিল, প্লাস ভোটিং পার্টের জন্য একটু একস্ট্রা। এখন AJAX রিকোয়েস্ট বানাই। আমাদের "people/_person.html.erb" এখন এরকম:
<%= button_to "Vote #{person.votes_count}", vote_person_path(person), remote: true %>
আমাদের কন্ট্রোলার অ্যাকশন JS রেসপন্স রিটার্ন করে, যেটি এরকম:
$("#<%= @person.id %>").html("<%= j render(partial: 'person', locals: {person: @person}) %>");
আপনি দেখতে পাচ্ছেন, আমরা শুধু প্রয়োজনীয় কন্টেন্ট রিপ্লেস করছি। আমরা একটি HTML ID দিয়ে একটি div-এ হুক করে সেটি রিপ্লেস করছি। অবশ্যই, আমরা আরও অপ্টিমাইজ করতে পারি শুধু বাটন কন্টেন্ট রিপ্লেস করে, কিন্তু এই পোস্টের উদ্দেশ্যে, পুরো পারশিয়াল রিপ্লেস করছি। রেজাল্ট?
Started POST "/people/1/vote" for 127.0.0.1 at 2020-01-21 14:52:56 +0530 Processing by PeopleController#vote as JS Person Load (0.2ms) SELECT "people".* FROM "people" WHERE "people"."id" = ? LIMIT ? [["id", 1], ["LIMIT", 1]] (0.1ms) begin transaction Person Exists (0.3ms) SELECT 1 AS one FROM "people" WHERE "people"."name" = ? AND ("people"."id" != ?) LIMIT ? [["name", "Deon Waelchi"], ["id", 1], ["LIMIT", 1]] SQL (0.4ms) UPDATE "people" SET "votes_count" = ?, "updated_at" = ? WHERE "people"."id" = ? [["votes_count", 2], ["updated_at", "2020-01-21 09:22:56.532281"], ["id", 1]] (1.6ms) commit transaction Rendering people/vote.js.erb (0.2ms) SELECT "profiles"."address" FROM "profiles" WHERE "profiles"."person_id" = ? [["person_id", 1]] Rendered people/_person.html.erb (3.2ms) Rendered people/vote.js.erb (6.3ms) Completed 200 OK in 31ms (Views: 14.6ms | ActiveRecord: 2.9ms)
৩০ms! শুধু এটুকু! কতটা সুন্দর?
প্রো টিপ: আপনি যদি HTML IDs এবং ক্লাসের ঝামেলায় না পড়তে চান কখন/কী রিপ্লেস করতে হবে তা বুঝতে, render_async জেম ব্যবহার করার কথা ভাবুন। এটি বক্সের বাইরে অনেক কঠিন কাজ হ্যান্ডেল করে।
৩. WebSockets ব্যবহার করুন
HTML রিলোডের একটি ভালো দিক হল এটি সার্ভার থেকে প্রতিবার ফ্রেশ কন্টেন্ট আনে। AJAX রিকোয়েস্টে, আপনি ছোট স্নিপেটের জন্য লেটেস্ট কন্টেন্ট দেখেন। WebSockets একটি দুর্দান্ত টেকনোলজি যা আপনার সার্ভারকে ক্লায়েন্টে আপডেট পুশ করতে দেয়, ক্লায়েন্ট নতুন ইনফরমেশন রিকোয়েস্ট করার বদলে। এটি ডাইনামিক ওয়েবপেজ বানাতে উপযোগী। কল্পনা করুন আপনার ওয়েবসাইটে একটি গেমের স্কোর ডিসপ্লে করতে হচ্ছে। নতুন কন্টেন্ট পেতে আপনি পারেন,
- ইউজারকে পুরো পেজ রিলোড করতে বলুন
- একটি রিলোড বাটন দিন যা শুধু স্কোর রিফ্রেশ করবে
- জাভাস্ক্রিপ্ট ব্যবহার করে প্রতি সেকেন্ডে ব্যাকএন্ড পোল করুন
- এটি সার্ভারকে পিং করে থাকবে এমনকি ডাটা চেঞ্জ না থাকলেও
- প্রতিটি ক্লায়েন্ট প্রতি সেকেন্ড কল করবে - সহজে সার্ভার ওভারওয়েল্ম করবে
- WebSockets ব্যবহার করুন!
ক্যাশিং
বেশিরাগ লোড টাইম ভিউ রেন্ডারিং-এ খরচ হয়। এতে সব CSS, JS এবং ইমেজ লোডিং, ERB ফাইল থেকে HTML রেন্ডারিং এবং আরও অনেক কিছু অন্তর্ভুক্ত। লোড টাইমের একটি বড় অংশ কমানোর একটি উপায় হল আপনার অ্যাপ্লিকেশনের যে অংশগুলো কিছু সময়ের জন্য স্ট্যাটিক থাকবে বা একটি ইভেন্ট পর্যন্ত, সেগুলো চিহ্নিত করা। আমাদের উদাহরণে, এটি স্পষ্ট যে কেউ ভোট না দেয়া পর্যন্ত, হোম পেজ সবার জন্য প্রায় একই দেখাবে (বর্তমানে ইউজারদের অ্যাড্রেস এডিট করার অপশন নেই)। চলুন পুরো "home.html.erb" পেজ ক্যাশ করি একটি ইভেন্ট (ভোট) হলে পর্যন্ত। Dalli জেম ব্যবহার করব। এটি Memcached ব্যবহার করে দ্রুত ইনফরমেশনের ফ্র্যাগমেন্ট স্টোর এবং রিট্রিভ করে। Memcached-এ স্টোরেজের জন্য কোনো ডাটা টাইপ নেই, তাই আপনি মূলত যা ইচ্ছে স্টোর করতে পারেন।
১. ভিউ ক্যাশিং
২০০০ রেকর্ডের জন্য ক্যাশিং ছাড়া লোড টাইম ৩৫০০ms! "home.html.erb"-এ সব কিছু ক্যাশ করি। এটি সহজ,
<% cache do %> <ul> <% @people.each do |person| %> <li id="<%= person.id %>"><%= render person %></li> <% end %> </ul> <% end %>
এরপর Dalli জেম ইনস্টল করুন এবং "development.rb"-এ ক্যাশ স্টোর পরিবর্তন করুন:
config.cache_store = :dalli_store
তাহলে, আপনি যদি Mac বা Linux-এ হন, সহজে Memcached সার্ভিস স্টার্ট করুন:
memcached -vv
এখন রিলোড করুন!! এটি লাগল প্রায় ৫৩৭ms! সেটি ৭x স্পিড ইমপ্রুভমেন্ট! আপনি দেখবেন MySQL কুয়েরি খুব কম হয়েছে কারণ পুরো HTML Memcached-এ স্টোর হয়েছিল এবং সেখান থেকে পড়া হয়েছে, ডাটাবেস কখনো পিং করা হয়নি। আপনার অ্যাপ্লিকেশন লগে দেখবেন পুরো পেজ ক্যাশ থেকে রিড হয়েছে। এই উদাহরণটা ক্যাশিং-এর সারফেস মাত্র ছুঁয়েছে। আপনি পারশিয়াল রেন্ডারিং ক্যাশ করতে পারেন প্রতিটি পেরসন অবজেক্টে স্কোপ করে (এটিকে ফ্র্যাগমেন্ট ক্যাশিং বলা হয়) অথবা পুরো কালেকশন ক্যাশ করতে পারেন (এটিকে কালেকশন ক্যাশিং বলা হয়)। আরও নেস্টেড ভিউ রেন্ডারিংয়ের জন্য, রাশিয়ান ডল ক্যাশিং পারফর্ম করতে পারেন।
২. ডাটাবেস কুয়েরি ক্যাশিং
ভিউ স্পিড বাড়ানোর আরেকটি অপ্টিমাইজেশন হল জটিল ডাটাবেস কুয়েরি ক্যাশ করা। যদি আপনার অ্যাপ স্ট্যাটস এবং এনালিটিক্স দেখায়, তাহলে সম্ভবত প্রতিটি মেট্রিক ক্যালকুলেট করার জন্য আপনি জটিল ডাটাবেস কুয়েরি পারফর্ম করছেন। আপনি আউটপুট Memcached-এ স্টোর করতে পারেন এবং একটি টাইমআউট অ্যাসাইন করতে পারেন। এর মানে, টাইমআউটের পর, ক্যালকুলেশন আবার হবে এবং পরবর্তী ৮ ঘন্টার জন্য ক্যাশে স্টোর হবে। উদাহরণস্বরূপ, ধরি অ্যাপ্লিকেশনের একটি ইউজারের টিম সাইজ দেখাতে হবে। এটি একটি জটিল ক্যালকুলেশন হতে পারে ডায়রেক্ট রিপোর্টি, আউটসোর্সড কনসালট্যান্টের কাউন্ট এবং আরও অনেক কিছু জড়িত। একই ক্যালকুলেশন বারবার রিপিট করার বদলে, আপনি ক্যাশ করতে পারেন!
def team_size Rails.cache.fetch(:team_size, expires_in: 8.hour) do analytics_client = AnalyticsClient.query!(self) analytics_client.team_size end end
এই ক্যাশ ৮ ঘন্টা পর অটোমেটিক্যালি এক্সপায়ার হবে। একবার এক্সপায়ার হলে, ক্যালকুলেশন আবার হবে এবং লেটেস্ট মান পরবর্তী ৮ ঘন্টার জন্য ক্যাশে থাকবে।
৩. ডাটাবেস ইন্ডেক্স
ইন্ডেক্স ব্যবহার করে কুয়েরি স্পিড আপ করা যায়। একটি পেরসনের সব অ্যাড্রেস ফেচ করার সহজ কুয়েরি,
person.addresses এই কুয়েরি Address টেবিলকে বলছে সব অ্যাড্রেস রিটার্ন করতে যেখানে person_id কলাম person.id। ইন্ডেক্স ছাড়া, ডাটাবেসকে প্রতিটি রো আলাদা আলাদা ইন্সপেক্ট করতে হয় চেক করতে.matches person.id। কিন্তু ইন্ডেক্স থাকলে, ডাটাবেসের पास থাকে একটি লিস্ট যেটি ম্যাচ করে নির্দিষ্ট person.id। ডাটাবেস ইন্ডেক্স সম্পর্কে আরও জানতে একটা চমৎকার রিসোর্স!
সারাংশ
এই পোস্টে, আমরা এক্সপ্লোর করেছি কিভাবে আপনার রেলস অ্যাপের ভিউ পারফরম্যান্স উন্নত করা যায় ডাটাবেস ইউটিলাইজেশন উন্নত করে, থার্ড-পার্টি টুল এবং সার্ভিস ব্যবহার করে এবং ইউজার কী দেখবে সেটি রেস্ট্রিক্ট করে। আপনি যদি আপনার অ্যাপের পারফরম্যান্স উন্নত করতে চান, সিম্পল শুরু করুন এবং চলমান মাপতে থাকুন! ডাটাবেস কুয়েরি ক্লিন আপ করুন, তারপর যেখানো সম্ভব AJAX রিকোয়েস্ট তৈরি করুন, এবং সর্বশেষ সম্ভবত সব ভিউ ক্যাশ করুন। তারপর WebSockets এবং ডাটাবেস ক্যাশিং-এ যান। তবে সতর্ক থাকুন—অপ্টিমাইজেশন একটি স্লিপারি স্লোপ। আপনি নিজেকে আমার মত অ্যাডিক্টেড পেতে পারেন! P.S. প্রোডাকশনে আপনার রেলস অ্যাপের পারফরম্যান্স মনিটর করতে, AppSignal-এর APM চেক আউট করুন - রুবি ডেভস দ্বারা রুবি ডেভদের জন্য তৈরি। 🚀