أفضل طريقة للجمع بين التخزين المؤقت للأجزاء والكائنات في memcached و Rails

StackOverflow https://stackoverflow.com/questions/1034910

سؤال

لنفترض أن لديك جزءًا من الصفحة يعرض أحدث المشاركات، وتنتهي صلاحيته خلال 30 دقيقة.أنا أستخدم القضبان هنا.

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  ...
<% end %>

من الواضح أنك لا تحتاج إلى إجراء بحث في قاعدة البيانات للحصول على أحدث المنشورات إذا كان الجزء موجودًا، لذلك يجب أن تكون قادرًا على تجنب هذا العبء أيضًا.

ما أفعله الآن هو شيء مثل هذا في وحدة التحكم والذي يبدو أنه يعمل:

unless Rails.cache.exist? "views/recent_posts"
  @posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end

هل هذا افضل طريق؟هل هو آمن؟

شيء واحد لا أفهمه هو سبب وجود المفتاح "recent_posts"للشظية و"views/recent_posts"عند التحقق لاحقًا، لكنني توصلت إلى هذا بعد المشاهدة memcached -vv لمعرفة ما كان يستخدم.كما أنني لا أحب الازدواجية في الإدخال اليدوي "recent_posts"، فمن الأفضل الاحتفاظ بذلك في مكان واحد.

أفكار؟

هل كانت مفيدة؟

المحلول

إيفان ويفر البرنامج المساعد الانترلوك يحل هذه المشكلة.

يمكنك أيضًا تنفيذ شيء مثل هذا بنفسك بسهولة إذا كنت بحاجة إلى سلوك مختلف، مثل المزيد من التحكم الدقيق.الفكرة الأساسية هي تغليف كود وحدة التحكم الخاصة بك في كتلة يتم تنفيذها فعليًا فقط إذا كان العرض يحتاج إلى تلك البيانات:

# in FooController#show
@foo_finder = lambda{ Foo.find_slow_stuff }

# in foo/show.html.erb
cache 'foo_slow_stuff' do
  @foo_finder.call.each do 
    ...
  end
end

إذا كنت على دراية بأساسيات البرمجة التعريفية لـ Ruby، فمن السهل إنهاء ذلك في واجهة برمجة تطبيقات أكثر نظافة حسب ذوقك.

يعد هذا أفضل من وضع رمز الباحث مباشرةً في العرض:

  • يحتفظ برمز الباحث حيث يتوقعه المطورون حسب الاتفاقية
  • يبقي العرض جاهلاً باسم/طريقة النموذج، مما يسمح بإعادة استخدام العرض بشكل أكبر

أعتقد أن Cache_fu قد يكون له وظيفة مماثلة في أحد إصداراته/شوكاته، لكن لا يمكنني تذكره على وجه التحديد.

ترتبط الميزة التي تحصل عليها من memcached بشكل مباشر بمعدل ضربات ذاكرة التخزين المؤقت.احرص على عدم إهدار سعة ذاكرة التخزين المؤقت الخاصة بك والتسبب في حدوث أخطاء غير ضرورية عن طريق تخزين نفس المحتوى مؤقتًا عدة مرات.على سبيل المثال، لا تقم بتخزين مجموعة من كائنات السجل بالإضافة إلى جزء html الخاص بها في نفس الوقت.بشكل عام، سيوفر التخزين المؤقت للأجزاء أفضل أداء، لكنه يعتمد حقًا على تفاصيل التطبيق الخاص بك.

نصائح أخرى

وماذا يحدث في حال انتهاء صلاحية ذاكرة التخزين المؤقت بين الوقت الذي تحقق له في وحدة تحكم والوقت انها بيينغ فحص في تقديم الرأي؟

وأود الإدلاء طريقة جديدة في النموذج:

  class Post
    def self.recent(count)
      find(:all, :limit=> count, :order=>"updated_at DESC")
    end
  end

وثم استخدام ذلك في رأي:

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  <% Post.recent(20).each do |post| %>
     ...
  <% end %>
<% end %>

لوضوح، هل يمكن أن تنظر أيضا في تحريك تقديم وظيفة الأخيرة إلى تلقاء نفسها جزئية:

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  <%= render :partial => "recent_post", :collection => Post.recent(20) %>
<% end %>

ويمكنك أيضا أن ننظر إلى

جزء مخبأ مستندات

والتي تسمح لك ان تفعل هذا:

<% cache("recent_posts", :expires_in => 30.minutes) do %>
  ...
<% end %>

والمراقب المالي

unless fragment_exist?("recent_posts")
  @posts = Post.find(:all, :limit=>20, :order=>"updated_at DESC")
end

وعلى الرغم من أنني أعترف قضية DRY تزال تطل برأسها تحتاج إلى اسم المفتاح في مكانين. أنا عادة القيام بذلك على غرار كيف اقترح لارس ولكن ذلك يعتمد حقا على الذوق. مطورين آخرين وأنا أعلم العصا مع فحص جزء موجودة.

تحديث:

إذا نظرتم الى مستندات جزء، يمكنك أن ترى كيف يتخلص ذلك من الحاجة البادئة الرأي:

# File vendor/rails/actionpack/lib/action_controller/caching/fragments.rb, line 33
def fragment_cache_key(key)
  ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end

ولارس يجعل نقطة جيدة حقا ما يجري هناك فرصة ضئيلة للفشل باستخدام:

unless fragment_exist?("recent_posts")

ولأن هناك فجوة بين عند التحقق من ذاكرة التخزين المؤقت وعند استخدام ذاكرة التخزين المؤقت.

والبرنامج المساعد أن جايسون يذكر (الانترلوك) يعالج هذا برشاقة جدا بافتراض أنه إذا كنت التحقق من وجود شظية، ثم هل من المحتمل أيضا استخدام جزء وبالتالي بتخزين محتوى محليا. يمكنني استخدام الانترلوك لهذه الأسباب بالذات.

ومثلما قطعة من الفكر:

وفي وحدة التحكم تطبيق تعريف

def when_fragment_expired( name, time_options = nil )
        # idea of avoiding race conditions
        # downside: needs 2 cache lookups
        # in view we actually cache indefinetely 
        # but we expire with a 2nd fragment in the controller which is expired time based
        return if ActionController::Base.cache_store.exist?( 'fragments/' + name ) && ActionController::Base.cache_store.exist?( fragment_cache_key( name ) )

        # the time_fraqgment_cache uses different time options
        time_options = time_options - Time.now if time_options.is_a?( Time )

        # set an artificial fragment which expires after given time
        ActionController::Base.cache_store.write("fragments/" + name, 1, :expires_in => time_options )

        ActionController::Base.cache_store.delete( "views/"+name )        
        yield    
  end

وبعد ذلك في أي استخدام العمل

    def index
when_fragment_expired "cache_key", 5.minutes
@object = YourObject.expensive_operations
end
end

ونظرا

cache "cache_key" do
view_code
end
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top