কম্পিউটার

জটিল ডেটা মডেল ক্যাশে করার একটি দ্রুত উপায়

যখন আপনার ডেটা মডেল জটিল হয়ে যায়, এবং আপনার APIগুলি সেই দুঃখজনক 1 সেকেন্ডের প্রতিক্রিয়ার সময় আঘাত করে, তখন সাধারণত একটি সহজ সমাধান থাকে::includes . যখন আপনি আপনার মডেলের অ্যাসোসিয়েশনগুলি প্রিলোড করেন, তখন আপনি এতগুলি SQL কল করবেন না৷ এবং এটি আপনার এক টন সময় বাঁচাতে পারে।

কিন্তু তারপরে আপনার সাইটটি আবার ধীর হয়ে যায় এবং আপনি প্রতিক্রিয়া ক্যাশে করার কথা ভাবেন। এবং এখন আপনি একটি সমস্যা আছে. কারণ আপনি যদি ক্যাশে থেকে প্রতিক্রিয়া পেতে চান:

results = {lawyer_1: 1, lawyer_2: 2, lawyer_3: 3}
cached_objects = Rails.cache.fetch_multi(results.keys) do |key|
  Lawyer.find(results[key]).as_json
end

আপনি এখন আপনার সমস্ত :includes হারিয়ে ফেলেছেন . আপনি উভয় পেতে পারেন? আপনি কীভাবে আপনার ক্যাশে করা অবজেক্টগুলির জন্য দ্রুত প্রতিক্রিয়া পাবেন, এবং এখনও ক্যাশে না থাকা বস্তুগুলিকে দ্রুত লোড করবেন?

অনেক কিছু করার আছে, তাই এটা নিয়ে চিন্তা করা কঠিন। আপনি যখন সমস্যাটিকে ছোট ছোট টুকরো করে বিভক্ত করেন এবং একটি সাধারণ পরবর্তী পদক্ষেপ নিয়ে আসেন তখন এটি সহজ হয়৷

সুতরাং আপনি প্রথম জিনিস কি করতে পারেন? অনেক কিছু করার জন্য, আপনার ক্যাশে কোন বস্তুগুলি আছে তা আপনাকে জানতে হবে এবং কোনটি আপনাকে এখনও খুঁজে বের করতে হবে৷

ক্যাশ করাকে আনক্যাশেড থেকে আলাদা করুন

সুতরাং, বলুন আপনার কাছে একগুচ্ছ ক্যাশে কী আছে:

cache_keys = [:key_1, :key_2, :key_3]

আপনি কিভাবে বলতে পারেন এর মধ্যে কোনটি ক্যাশে আছে?

ActiveSupport::Cache read_multi নামে একটি সহজ পদ্ধতি আছে :

# When only lawyer_1 is cached

cache_keys = [:lawyer_1, :lawyer_2, :lawyer_3]
Rails.cache.read_multi(cache_keys) # => {:lawyer_1 => {"id": 1, "name": "Bob the Lawyer"} }

read_multi {key: value} এর একটি হ্যাশ ফেরত দেয় ক্যাশে পাওয়া প্রতিটি কী জন্য. কিন্তু আপনি কিভাবে সমস্ত চাবি খুঁজে পাবেন যেগুলি না ক্যাশে? আপনি এটি সহজভাবে করতে পারেন:সমস্ত ক্যাশে কীগুলি লুপ করুন এবং খুঁজে বের করুন কোনটি হ্যাশে নেই যেটি read_multi ফেরত:

cache_keys = [:lawyer_1, :lawyer_2, :lawyer_3]
uncached_keys = []

cached_keys_with_values = Rails.cache.read_multi(cache_keys)

cache_keys.each do |key|
  uncached_keys << key unless cached_keys_with_values.has_key?(key)
end

তো, এখন আপনার কাছে কি আছে?

  • সমস্ত ক্যাশে কীগুলির একটি অ্যারে যা আপনি বস্তুর জন্য চেয়েছিলেন।
  • {key: value} এর একটি হ্যাশ আপনি ক্যাশে পাওয়া প্রতিটি বস্তুর জন্য জোড়া।
  • ক্যাশে না থাকা কীগুলির একটি তালিকা৷

এবং আপনার পরবর্তী কি দরকার?

  • মানগুলি ক্যাশে না থাকা কীগুলির জন্য। একযোগে সবগুলো আনার পছন্দ।

এটি আপনার পরবর্তী পদক্ষেপ।

আনক্যাশেড মানগুলি আগে থেকে লোড করুন

শীঘ্রই, আপনাকে একটি ক্যাশে কী ব্যবহার করে একটি বস্তু খুঁজে বের করতে হবে। জিনিসগুলিকে সহজ করার জন্য, আপনি কোডটিকে এমন কিছুতে পরিবর্তন করতে পারেন:

cache_identifiers = {lawyer_1: 1, lawyer_2: 2, lawyer_3: 3}
cache_keys = cache_identifiers.keys
uncached_keys = []

cached_keys_with_values = Rails.cache.read_multi(cache_keys)

cache_keys.each do |key|
  uncached_keys << key unless cached_keys_with_values.has_key?(key)
end

তাই cache_identifiers এখন ক্যাশে কী এবং ট্র্যাক রাখে আনার জন্য অবজেক্ট আইডি।

এখন, আপনার আনক্যাশড কীগুলির সাথে:

uncached_keys # => [:lawyer_2, :lawyer_3]

এবং আপনার cache_identifiers হ্যাশ:

cache_identifiers # => {lawyer_1: 1, lawyer_2: 2, lawyer_3: 3}

আপনি একবারে এই সমস্ত অবজেক্ট আনতে, প্রিলোড করতে এবং সিরিয়ালাইজ করতে পারেন:

uncached_ids = uncached_keys.map { |key| cache_identifiers[key] }
uncached_lawyers = Lawyer.where(id: uncached_ids)
                         .includes([:address, :practice_areas, :awards, ...])
                         .map(&:as_json))

তাহলে এখন আপনার কাছে কি আছে?

  • সমস্ত ক্যাশে কীগুলির একটি অ্যারে যা আপনি বস্তুর সাথে শুরু করতে চেয়েছিলেন।
  • {key: value} এর একটি হ্যাশ ক্যাশে পাওয়া প্রতিটি বস্তুর জন্য জোড়া।
  • ক্যাশে না থাকা কীগুলির একটি তালিকা৷
  • সকল মান যা ক্যাশে পাওয়া যায়নি।

এবং আপনার পরবর্তী কি দরকার?

  • আপনি এইমাত্র যে মানগুলি নিয়ে এসেছেন তা ক্যাশে করতে, যাতে পরের বার আপনাকে এই পুরো প্রক্রিয়াটির মধ্য দিয়ে যেতে হবে না৷
  • আপনার সমস্ত বস্তুর চূড়ান্ত তালিকা, সেগুলি ক্যাশে থেকে এসেছে কিনা।

আনক্যাশেড মানগুলি ক্যাশ করুন

আপনার দুটি তালিকা রয়েছে:একটি ক্যাশড কীগুলির তালিকা এবং অন্যটি আনক্যাশড মানের তালিকা৷ কিন্তু সেগুলিকে ক্যাশে করা, আপনার যদি একটি থাকত তাহলে এটি আরও সহজ হবে৷ [key, value] এর তালিকা জোড়া, যাতে আপনার value এটির key এর ঠিক পাশে . এটি আমার প্রিয় পদ্ধতিগুলির একটি, zip ব্যবহার করার একটি অজুহাত :

[1, 2, 3].zip(["a", "b", "c"]) # => [[1, "a"], [2, "b"], [3, "c"]]

zip সহ , আপনি সহজেই আপনার আনা মান ক্যাশে করতে পারেন:

uncached_keys.zip(uncached_lawyers).each do |key, value|
  Rails.cache.write(key, value)
end

এখন আপনার কি আছে?

  • সমস্ত ক্যাশে কীগুলির একটি অ্যারে যা আপনি বস্তুর সাথে শুরু করতে চেয়েছিলেন।
  • {key: value} এর একটি হ্যাশ ক্যাশে পাওয়া প্রতিটি বস্তুর জন্য জোড়া।
  • পূর্বে-আনক্যাশেড মানগুলির একটি তালিকা যা আপনি এইমাত্র ক্যাশে করেছেন৷

এবং আপনার এখনও কি দরকার?

  • আপনার সমস্ত অবজেক্টের একটি বড় তালিকা, সেগুলি ক্যাশে থেকে এসেছে কি না।

এটি সব একসাথে আনুন

এখন, আপনার কাছে ক্যাশে কীগুলির একটি অর্ডার করা তালিকা রয়েছে:

cache_keys = cache_identifiers.keys

আপনি ক্যাশে থেকে আনা বস্তুর তালিকা:

cached_keys_with_values = Rails.cache.read_multi(cache_keys)

এবং আপনার অবজেক্টের তালিকা যা আপনি এইমাত্র ডাটাবেস থেকে ধরেছেন:

uncached_ids = uncached_keys.map { |key| cache_identifiers[key] }
uncached_lawyers = Lawyer.where(id: uncached_ids)
                         .includes([:address, :practice_areas, :awards, ...])
                         .map(&:as_json))

এখন আপনার সবকিছু একত্রিত করার জন্য একটি শেষ লুপ দরকার:

results = []
cache_keys.each do |key|
  results << cache_keys_with_values[key] || uncached_lawyers.shift
end

অর্থাৎ, প্রতিটি ক্যাশে কী-এর জন্য, আপনি সেই কীটির জন্য ক্যাশে পাওয়া বস্তুটি ধরবেন। যদি সেই কীটি মূলত ক্যাশে না থাকে, তাহলে আপনি ডাটাবেস থেকে টানা পরবর্তী বস্তুটি ধরবেন।

এর পরে, আপনার কাজ শেষ!

পুরো জিনিসটি কেমন দেখাচ্ছে তা এখানে:

cache_identifiers = {lawyer_1: 1, lawyer_2: 2, lawyer_3: 3}
cache_keys = cache_identifiers.keys
uncached_keys = []

# Fetch the cached values from the cache
cached_keys_with_values = Rails.cache.read_multi(cache_keys)

# Create the list of keys that weren't in the cache
cache_keys.each do |key|
  uncached_keys << key unless cached_keys_with_values.has_key?(key)
end

# Fetch all the uncached values, in bulk
uncached_ids = uncached_keys.map { |key| cache_identifiers[key] }
uncached_lawyers = Lawyer.where(id: uncached_ids)
                         .includes([:address, :practice_areas, :awards, ...])
                         .map(&:as_json))

# Write the uncached values back to the cache
uncached_keys.zip(uncached_lawyers).each do |key, value|
  Rails.cache.write(key, value)
end

# Create our final result set from the cached and uncached values
results = []
cache_keys.each do |key|
  results << cache_keys_with_values[key] || uncached_lawyers.shift
end
results

এটা মূল্য ছিল? হতে পারে. এটা অনেক কোড। কিন্তু আপনি যদি প্রচুর অ্যাসোসিয়েশনের সাথে অবজেক্ট ক্যাশে করেন, তাহলে এটি আপনাকে কয়েক ডজন বা শত শত SQL কল বাঁচাতে পারে। এবং এটি আপনার API প্রতিক্রিয়াগুলি থেকে এক টন সময় কমিয়ে দিতে পারে৷

Avvo-এ, এই প্যাটার্নটি অবিশ্বাস্যভাবে উপযোগী হয়েছে:আমাদের অনেক JSON API গুলি অবিশ্বাস্যভাবে ক্যাশে করা প্রতিক্রিয়াগুলি ফেরত দিতে এটি ব্যবহার করে।

প্যাটার্নটি এতটাই উপযোগী হয়েছে যে আমি এটিকে এনক্যাপসুলেট করার জন্য একটি রত্ন লিখেছিলাম যার নাম bulk_cache_fetcher৷ তাই আপনি যদি কখনও নিজেকে বড়, জটিল ডেটা মডেল ক্যাশে করার চেষ্টা করেন, তাহলে একবার চেষ্টা করে দেখুন!


  1. স্কেল প্রাইমারে ক্যাশিং থেকে ক্যাশিংয়ের ভূমিকা

  2. ক্যাশে সামঞ্জস্য বজায় রাখার তিনটি উপায়

  3. Windows 10 এ Chrome-এ কুকিজ এবং ক্যাশে সাফ করার দ্রুততম উপায়

  4. স্টেগানোগ্রাফি:ম্যালওয়্যার ছড়ানোর একটি নতুন উপায়