loading...

[Redis] How To: Remove Redis Keys without TTL (in Ruby)

pikachuexe profile image PikachuEXE ・1 min read

Background

Going to update our caching Redis Server config
to have maxmemory-policy become volatile-ttl
Doc: https://redis.io/topics/lru-cache

Since we use allkeys-lru before and have keys without TTL set (TTL key would return -1)

Those keys are required to be removed or they will never be evicted after config update

Code

::Rails.application.config.cache_store.redis.with do |c|
  cursor = "0"
  count = 0
  # Fetch keys in batches using SCAN to avoid blocking the Redis server.
  begin
    cursor, keys = c.scan(cursor, count: 1000)
    all_key_count = c.dbsize

    puts("cursor: #{cursor}")
    puts("#{count}/#{all_key_count} keys scanned")

    keys_del = keys.each_with_object([]) do |key, keys_to_be_del|
      # Won't expire
      if !key.match?("sprockets|sidekiq|sessions") && c.ttl(key) == -1
        keys_to_be_del.push(key)
        puts("to be deleted: <#{key}>")
      end
    end
    c.del(*keys_del) unless keys_del.empty?

    count += keys.size
    puts("#{count} keys scanned")
  end until cursor == "0"
end

Enter fullscreen mode Exit fullscreen mode

cursor, keys = c.scan(cursor, count: 1000)
SCAN is good, KEYS is bad

!key.match?("sprockets|sidekiq|sessions")
This is due to I am testing my code in development and the Redis server is used for sprockets caching, (rack?) sessions, sidekiq and caching
Feel free to remove it if you want

puts("to be deleted: <#{key}>")
Just another debugging code
I actually commented this code when running on production

c.del(*keys_del) unless keys_del.empty?
Time complexity: O(N)
You can choose to collect all keys then remove the keys in batches

Discussion

pic
Editor guide