0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

mem_cache_storeでdelete_matchedをできるようにする。

Last updated at Posted at 2020-12-21

mem_cache_storeはdelete_matchedをサポートしてません。なにか動的に変わるものをキーにIDなどを付与してキャッシュした時、まとめて消すことができず不便だったので、微妙ですが、モンキーパッチ書きました。

コード

module MemCacheStoreWithDeleteMatched
  MEM_CACHE_STORE_WITH_DELETE_MATCHED_KEYS = 'MemCacheStoreWithDeleteMatchedKeys'

  def delete_matched(matcher, options = nil)
    options = merged_options(options)
    options[:support_delete_matched] = true
    instrument(:delete_matched, matcher.inspect) do
      matcher = key_matcher(matcher, options)
      key_hash = fetch(MEM_CACHE_STORE_WITH_DELETE_MATCHED_KEYS) { {} }
      key_hash.keys.each do |key|
        delete_entry(key, **options) if key.match(matcher)
      end
    end
  end

  def write_entry(key, entry, **options)
    ret = super(key, entry, **options)
    return ret unless options[:support_delete_matched]

    keys = fetch(MEM_CACHE_STORE_WITH_DELETE_MATCHED_KEYS) { {} }
    keys[key] = nil
    write(MEM_CACHE_STORE_WITH_DELETE_MATCHED_KEYS, keys, support_delete_matched: false)
    ret
  end

  def delete_entry(key, **options)
    ret = super(key, **options)
    return ret unless options[:support_delete_matched]

    keys = fetch(MEM_CACHE_STORE_WITH_DELETE_MATCHED_KEYS) { {} }
    keys.delete(key)
    write(MEM_CACHE_STORE_WITH_DELETE_MATCHED_KEYS, keys, support_delete_matched: false)
    ret
  end
end

module ActiveSupport
  module Cache
    class MemCacheStore
      prepend MemCacheStoreWithDeleteMatched
    end
  end
end

解説

結構強引ですが、キャッシュの保存時にキーのリストをmemcachedに保存しちゃってます。正確には出し入れ時に検索を早くするためhashでしまってます。

保存・削除時にsupport_delete_matched: trueを渡すとキーが保存されてdelete_matchedで削除できるようになります。

Rails.cache.write(key, 'value', support_delete_matched: true)
Rails.cache.delete(key, support_delete_matched: true)

多分削除はdelete_matchedを使うのでdeleteは使わないと思いますが、deleteを呼ぶなら渡さないとキーにゴミが残ります。expireしたキャッシュのキーはそのままなのでリストに残ります。まあ、expireさせるならsupport_delete_matched: trueにすることもないと思いますが。

ちなみに、envで丸っとONにすることも可能です。

config.cache_store = :mem_cache_store, 'localhost:11211', { support_delete_matched: true }

mem_cache_storeはバックエンドでdaliを使用していますが、本家ではキーリストの書き込みにかかる時間でキャッシュの利点が損なわれるという理由で却下されてます。ただ、キャッシュって読み込み早ければそれでよくない?って気もしますが・・・まあ、使用用途にもよりますか。

確かに書き込みと削除はキャッシュの量が増えるとhashのシリアライズに時間がかかるので遅くなりますね。そこは注意してください。ベンチ載せておきます。

Bench

Benchmark.bm 10 do |r|
  r.report "support" do
    10000.times do |num|
      key = "foobar___________________________#{num}"
      Rails.cache.write(key, 'value', support_delete_matched: true)
      Rails.cache.delete(key, support_delete_matched: true)
    end
  end

  r.report "unsupport" do
    10000.times do |num|
      key = "foobar___________________________#{num}"
      Rails.cache.write(key, 'value')
      Rails.cache.delete(key)
    end
  end
end

結果

                 user     system      total        real
support      7.934330   0.920018   8.854348 (  8.901407)
unsupport    2.024292   0.253793   2.278085 (  2.287548)

状況によっては、削除時に、DBに接続して存在する可能性のあるキーを全部生成して削除を試みるのもアリですね。そういうこともあったので、オプションでON/OFFできるようにしてみました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?