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 d76622a..4bbf952 100644 --- a/lib/redis/namespace.rb +++ b/lib/redis/namespace.rb @@ -338,6 +338,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? @@ -599,5 +625,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 3d1a733..2836f8f 100644 --- a/spec/redis_spec.rb +++ b/spec/redis_spec.rb @@ -1053,4 +1053,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