जब आपका डेटा मॉडल जटिल हो जाता है, और आपके APIs उस दुखद 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 endpre>आपने अब अपने सभी
:includesखो दिए हैं। क्या आपके पास दोनों हो सकते हैं? आप अपने कैश्ड ऑब्जेक्ट्स के लिए तेज़ प्रतिक्रिया कैसे प्राप्त करते हैं, और फिर भी उन ऑब्जेक्ट्स को जल्दी से लोड करते हैं जो कैश में नहीं हैं?बहुत कुछ करना है, इसलिए इसके बारे में सोचना कठिन है। यह आसान होता है जब आप समस्या को छोटे टुकड़ों में तोड़ते हैं, और एक सरल अगला कदम लेकर आते हैं।
तो पहली चीज़ जो आप कर सकते हैं वह क्या है? कुछ भी करने के लिए, आपको यह जानना होगा कि कौन से ऑब्जेक्ट्स आपके कैश में हैं, और कौन से आपको अभी भी ढूंढने की आवश्यकता है।
कैश्ड को अनकैश्ड से अलग करें
तो, मान लें कि आपके पास कैश कुंजियों का एक गुच्छा है:
cache_keys = [:key_1, :key_2, :key_3]pre>आप कैसे बता सकते हैं कि इनमें से कौन सी कैश में हैं?
ActiveSupport::Cacheमें एक आसान मेथड है जिसेread_multiकहा जाता है:# जब केवल lawyer_1 कैश्ड है cache_keys = [:lawyer_1, :lawyer_2, :lawyer_3] Rails.cache.read_multi(cache_keys) # => {:lawyer_1 => {"id": 1, "name": "Bob the Lawyer"} }pre>
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) endpre>तो, अब आपके पास क्या है?
- उन सभी कैश कुंजियों की एक सरणी जिनके लिए आप ऑब्जेक्ट्स चाहते थे।
- कैश में मिले प्रत्येक ऑब्जेक्ट के लिए
{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) endpre>तो
cache_identifiersअब कैश कुंजी और लाने के लिए ऑब्जेक्ट आईडी दोनों को ट्रैक करता है।अब, अपनी अनकैश्ड कुंजियों के साथ:
uncached_keys # => [:lawyer_2, :lawyer_3]pre>और आपके
cache_identifiersहैश के साथ:cache_identifiers # => {lawyer_1: 1, lawyer_2: 2, lawyer_3: 3}pre>आप उन सभी ऑब्जेक्ट्स को एक साथ ला सकते हैं, प्रीलोड कर सकते हैं, और सीरियलाइज़ कर सकते हैं:
uncached_ids = uncached_keys.map { |key| cache_identifiers[key] } uncached_lawyers = Lawyer.where(id: uncached_ids) .includes([:address, :practice_areas, :awards, ...]) .map(&:as_json))pre>तो अब आपके पास क्या है?
- उन सभी कैश कुंजियों की एक सरणी जिनके लिए आप ऑब्जेक्ट्स चाहते थे शुरू में।
- कैश में मिले प्रत्येक ऑब्जेक्ट के लिए
{key: value}जोड़े का एक हैश। - कैश में नहीं मिली कुंजियों की एक सूची।
- कैश में नहीं मिले सभी मान।
और आपको आगे क्या चाहिए?
- आपके द्वारा अभी प्राप्त किए गए सभी मानों को कैश करें, ताकि आपको अगली बार इस पूरी प्रक्रिया से न गुजरना पड़े।
- आपके सभी ऑब्जेक्ट्स की अंतिम सूची, चाहे वे कैश से आए हों या नहीं।
अनकैश्ड मानों को कैश करें
आपके पास दो सूचियां हैं: अनकैश्ड कुंजियों में से एक और अनकैश्ड मानों में से एक। लेकिन उन्हें कैश करने के लिए, यह आसान होगा यदि आपके पास एक सूची [key, value] जोड़े की हो, ताकि आपका value अपने key के बगल में हो। यह मेरे पसंदीदा मेथड्स में से एक, zip का उपयोग करने का एक बहाना है:
[1, 2, 3].zip(["a", "b", "c"]) # => [[1, "a"], [2, "b"], [3, "c"]]pre>
zipके साथ, आप अपने प्राप्त मानों को आसानी से कैश कर सकते हैं:uncached_keys.zip(uncached_lawyers).each do |key, value| Rails.cache.write(key, value) endpre>अब आपके पास क्या है?
- उन सभी कैश कुंजियों की एक सरणी जिनके लिए आप ऑब्जेक्ट्स चाहते थे शुरू में।
- कैश में मिले प्रत्येक ऑब्जेक्ट के लिए
{key: value}जोड़े का एक हैश। - आपके द्वारा अभी कैश किए गए पूर्व-अनकैश्ड मानों की एक सूची।
और आपको अभी भी क्या चाहिए?
- आपके सभी ऑब्जेक्ट्स की एक बड़ी सूची, चाहे वे कैश से आए हों या नहीं।
इसे सब एक साथ लाएं
अब, आपके पास कैश कुंजियों की एक क्रमबद्ध सूची है:
cache_keys = cache_identifiers.keyspre>कैश से आपके द्वारा प्राप्त ऑब्जेक्ट्स की आपकी सूची:
cached_keys_with_values = Rails.cache.read_multi(cache_keys)pre>और आपके द्वारा अभी डेटाबेस से लिए गए ऑब्जेक्ट्स की आपकी सूची:
uncached_ids = uncached_keys.map { |key| cache_identifiers[key] } uncached_lawyers = Lawyer.where(id: uncached_ids) .includes([:address, :practice_areas, :awards, ...]) .map(&:as_json))pre>अब आपको बस सब कुछ एक साथ रखने के लिए एक अंतिम लूप की आवश्यकता है:
results = [] cache_keys.each do |key| results << cache_keys_with_values[key] || uncached_lawyers.shift endpre>यानी, प्रत्येक कैश कुंजी के लिए, आप उस कुंजी के लिए कैश में मिले ऑब्जेक्ट को पकड़ते हैं। यदि वह कुंजी मूल रूप से कैश में नहीं थी, तो आप डेटाबेस से निकाले गए अगले ऑब्जेक्ट को पकड़ते हैं।
उसके बाद, आप काम पूरा कर चुके हैं!
यहां पूरी चीज़ कैसी दिखती है:
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 # सभी अनकैश्ड मानों को बल्क में लाएं uncached_ids = uncached_keys.map { |key| cache_identifiers[key] } uncached_lawyers = Lawyer.where(id: uncached_ids) .includes([:address, :practice_areas, :awards, ...]) .map(&:as_json)) # अनकैश्ड मानों को वापस कैश में लिखें uncached_keys.zip(uncached_lawyers).each do |key, value| Rails.cache.write(key, value) end # कैश्ड और अनकैश्ड मानों से अपना अंतिम परिणाम सेट बनाएं results = [] cache_keys.each do |key| results << cache_keys_with_values[key] || uncached_lawyers.shift end resultspre>क्या यह इसके लायक था? शायद। यह बहुत सारा कोड है। लेकिन अगर आप बहुत सारे एसोसिएशन वाले ऑब्जेक्ट्स को कैश कर रहे हैं, तो यह आपको दर्जनों या सैकड़ों SQL कॉल बचा सकता है। और यह आपके API प्रतिक्रियाओं से बहुत समय कम कर सकता है।
Avvo में, यह पैटर्न अविश्वसनीय रूप से उपयोगी रहा है: हमारे बहुत सारे JSON APIs इसका उपयोग अविश्वसनीय रूप से तेजी से कैश्ड प्रतिक्रियाएं वापस करने के लिए करते हैं।
यह पैटर्न इतना उपयोगी रहा है कि मैंने इसे bulk_cache_fetcher नामक एक जेम में समाहित किया। तो अगर आप कभी खुद को बड़े, जटिल डेटा मॉडल को कैश करने की कोशिश करते हुए पाते हैं, तो इसे आज़माएं!