From f4b0c6b7ff2323dac73392aa71d1bac7bae6e1a8 Mon Sep 17 00:00:00 2001 From: fatkodima Date: Sun, 3 Jul 2022 19:28:14 +0300 Subject: [PATCH] Add ability to clear namespaces --- CHANGELOG.md | 3 +++ lib/redis/namespace.rb | 31 +++++++++++++++++++++++++++++++ spec/redis_spec.rb | 26 ++++++++++++++++++++++++++ 3 files changed, 60 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f5203a..148d5f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## x.y.z +- Add ability to clear namespaces (#202). This operation can run for a very long time if the namespace contains + lots of keys! It should be used in tests, or when the namespace is small enough and you are sure you know what you are doing. + ## 1.9.0 - Accept Proc as a namespace (#203) diff --git a/lib/redis/namespace.rb b/lib/redis/namespace.rb index 85172a7..cc29f26 100644 --- a/lib/redis/namespace.rb +++ b/lib/redis/namespace.rb @@ -330,6 +330,32 @@ def eval(*args) end ruby2_keywords(:eval) if respond_to?(:ruby2_keywords, true) + # This operation can run for a very long time if the namespace contains lots of keys! + # It should be used in tests, or when the namespace is small enough + # and you are sure you know what you are doing. + def clear + if warning? + warn("This operation can run for a very long time if the namespace contains lots of keys! " + + "It should be used in tests, or when the namespace is small enough " + + "and you are sure you know what you are doing.") + end + + batch_size = 1000 + + if supports_scan? + cursor = "0" + begin + cursor, keys = scan(cursor, count: batch_size) + del(*keys) unless keys.empty? + end until cursor == "0" + else + all_keys = keys("*") + all_keys.each_slice(batch_size) do |keys| + del(*keys) + end + end + end + ADMINISTRATIVE_COMMANDS.keys.each do |command| define_method(command) do |*args, &block| raise NoMethodError if deprecations? @@ -591,5 +617,10 @@ def create_enumerator(&block) Enumerator.new(&block) end end + + def supports_scan? + redis_version = @redis.info["redis_version"] + Gem::Version.new(redis_version) >= Gem::Version.new("2.8.0") + end end end diff --git a/spec/redis_spec.rb b/spec/redis_spec.rb index 49993d1..1e5848d 100644 --- a/spec/redis_spec.rb +++ b/spec/redis_spec.rb @@ -1048,4 +1048,30 @@ expect(sub_sub_namespaced.full_namespace).to eql("ns:sub1:sub2") end end + + describe :clear do + it "warns with helpful output" do + expect { @namespaced.clear }.to output(/can run for a very long time/).to_stderr + end + + it "should delete all the keys" do + @redis.set("foo", "bar") + @namespaced.mset("foo1", "bar", "foo2", "bar") + capture_stderr { @namespaced.clear } + + expect(@redis.keys).to eq ["foo"] + expect(@namespaced.keys).to be_empty + end + + it "should delete all the keys in older redis" do + allow(@redis).to receive(:info).and_return({ "redis_version" => "2.7.0" }) + + @redis.set("foo", "bar") + @namespaced.mset("foo1", "bar", "foo2", "bar") + capture_stderr { @namespaced.clear } + + expect(@redis.keys).to eq ["foo"] + expect(@namespaced.keys).to be_empty + end + end end