Pull to refresh

Эмуляция тегов в MemCacheStore

Reading time3 min
Views420
Здравствуй хабр.
Собственно начну без прелюдий, писать красиво не обучен, так что извиняйте сразу.

Долго я искал реально работающую, написанную на руби для рельсов, библиотечку, которая позволяла бы помечать тегами куски кеша.

Дак вот, уж больно мне понравилась реализация у Котерова. И решил собственно сегодня под конец пятницы переписать из php на ruby самый важный кусок. Я не претендую на лавры, просто может кому пригодится, т.к. готовых реализаций гугл мне не сообщил.

Copy Source | Copy HTML
  1. ActiveSupport::Cache::MemCacheStore.class_eval do
  2.  
  3.   def read_with_tags key, options = nil
  4.     # Data is saved in form of: [tagsWithVersionArray, anyData].
  5.     serialized = read_without_tags(key, options)
  6.     return nil unless serialized
  7.  
  8.     combined = Marshal::load(serialized)
  9.     return nil unless combined.is_a?(Array)
  10.  
  11.     # Test if all tags has the same version as when the slot was created
  12.     # (i.e. still not removed and then recreated).
  13.     if combined.first.is_a?(Hash) && !combined.first.empty?
  14.       all_tag_values = read_multi(combined.first.keys)
  15.       combined.first.each_pair do |tag, saved_tag_version|
  16.         actual_tag_version = all_tag_values[tag]
  17.         return nil if actual_tag_version != saved_tag_version
  18.       end
  19.     end
  20.  
  21.     combined.last
  22.   end
  23.  
  24.   def write_with_tags key, value, options = nil
  25.     tags = options.is_a?(Hash) ? options.delete(:tags) : []
  26.     tags = tags.to_a.map{|item| "#{item[0]}:#{item[1]}"} if tags.is_a?(Hash)
  27.  
  28.     # Save/update tags as usual infinite keys with value of tag version.
  29.     # If the tag already exists, do not rewrite it.
  30.     tags_with_version = {}
  31.     tags.each do |tag|
  32.       tag_key = tag.to_s
  33.       tag_version = read_without_tags tag_key
  34.       if tag_version.nil?
  35.         tag_version = generate_new_tag_version
  36.         write_without_tags tag_key, tag_version
  37.       end
  38.       tags_with_version[tag_key] = tag_version
  39.     end if tags.is_a?(Array) && !tags.empty?
  40.  
  41.     write_without_tags key, Marshal::dump([tags_with_version, value]), options
  42.   end
  43.  
  44.   def delete_by_tags tags, options = nil
  45.     return nil if tags.blank?
  46.     tags = tags.to_a.map{|item| "#{item[0]}:#{item[1]}"} if tags.is_a?(Hash)
  47.     tags.each{|tag| delete(tag, options)}
  48.   end
  49.  
  50.   def generate_new_tag_version
  51.     @@tag_version_counter ||=  0
  52.     @@tag_version_counter += 1
  53.     Digest::SHA1.hexdigest("#{Time.now.to_f}_#{rand}_#{@@tag_version_counter}" )
  54.   end
  55.  
  56.  
  57.   alias_method_chain :read, :tags
  58.   alias_method_chain :write, :tags
  59. end
  60.  
  61.  
  62. module ActionController
  63.   module Caching
  64.     module Fragments
  65.       def expire_fragments_by_tags *args
  66.         return unless cache_configured?
  67.         tags = args.extract_options!
  68.         cache_store.delete_by_tags tags
  69.       end
  70.     end
  71.   end
  72. end


И помещаем это всё в /config/initializers/mem_cache_tags_store.rb
За код прошу не пинать, т.к. на руби пишу всего полгода, до этого на php лет пять, на который теперь без слёз смотреть не могу :)

Ну и собственно работает это вот как-то так:
Rails.cache.write 'key', 'value', {:tags => ['company_1', 'user_2']}
Rails.cache.read 'key'
=> "value"
Rails.cache.delete_by_tags ['user_2']
Rails.cache.read 'key'
=> nil
Tags:
Hubs:
Total votes 4: ↑3 and ↓1+2
Comments1

Articles