redis-cleaner 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +8 -0
- data/README.md +57 -0
- data/Rakefile +8 -0
- data/lib/redis_cleaner.rb +83 -0
- data/redis-cleaner.gemspec +18 -0
- data/spec/redis-cleaner/redis_cleaner_spec.rb +116 -0
- data/spec/spec_helper.rb +12 -0
- metadata +93 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
Redis-Cleaner [](https://travis-ci.org/lloydmeta/redis-cleaner) [](https://codeclimate.com/github/lloydmeta/redis-cleaner)
|
2
|
+
-------------
|
3
|
+
|
4
|
+
A simple way of cleaning up a large number of Redis keys via [pattern matching](http://redis.io/commands/keys)
|
5
|
+
|
6
|
+
Example Usage
|
7
|
+
=========
|
8
|
+
|
9
|
+
Instantiating a redis_cleaner
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
# Timeout needs to be set, because KEYS is run on the Redis-server
|
13
|
+
# side, which is potentially slow -> O(n) where n is the number of keys
|
14
|
+
redis_config = {
|
15
|
+
host: "127.0.0.1",
|
16
|
+
port: 6390,
|
17
|
+
timeout: 60
|
18
|
+
}
|
19
|
+
|
20
|
+
redis_cleaner = RedisKeyCleaner.new(Redis.new(redis_config), "./borked_keys")
|
21
|
+
```
|
22
|
+
|
23
|
+
Separate dumping and cleaning
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
redis_cleaner.dump_matching_keys_to_temp_file("resque:resque-retry*") #<-- can be skipped if you already have a file to read from
|
27
|
+
cleanup_job_stats = redis_cleaner.delete_keys_in_temp_file(verbose: false)
|
28
|
+
puts "Deleted #{cleanup_job_stats[:deleted_keys_count]} keys out of #{cleanup_job_stats[:total_keys_count]}"
|
29
|
+
```
|
30
|
+
|
31
|
+
Do everything in one go
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
redis_cleaner.dump_matching_keys_to_temp_file("resque:resque-retry*", delete_temp_file: false, verbose: false, batch_size: 200)
|
35
|
+
```
|
36
|
+
|
37
|
+
## License
|
38
|
+
|
39
|
+
Copyright (c) 2013 by Lloyd Chan
|
40
|
+
|
41
|
+
Permission is hereby granted, free of charge, to any person obtaining a
|
42
|
+
copy of this software and associated documentation files (the
|
43
|
+
"Software"), to deal in the Software without restriction, including
|
44
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
45
|
+
distribute, and to permit persons to whom the Software is furnished to do so, subject to
|
46
|
+
the following conditions:
|
47
|
+
|
48
|
+
The above copyright notice and this permission notice shall be included
|
49
|
+
in all copies or substantial portions of the Software.
|
50
|
+
|
51
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
52
|
+
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
53
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
54
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
55
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
56
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
57
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
#encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler/setup'
|
5
|
+
|
6
|
+
class RedisCleaner
|
7
|
+
|
8
|
+
attr_accessor :redis, :temp_file_path
|
9
|
+
|
10
|
+
def initialize(redis, temp_file_path)
|
11
|
+
self.redis = redis
|
12
|
+
self.temp_file_path = temp_file_path
|
13
|
+
end
|
14
|
+
|
15
|
+
# returns number of keys deleted if
|
16
|
+
# everything was succesfully deleted otherwise
|
17
|
+
# return false
|
18
|
+
def remove_from_redis(keys_group = [])
|
19
|
+
if redis.del(keys_group) == keys_group.size
|
20
|
+
keys_group.size
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# def delete_in_groups
|
27
|
+
# end
|
28
|
+
# deleted_keys_count = 0
|
29
|
+
# total_keys_count = 0
|
30
|
+
|
31
|
+
# takes params inside a hash
|
32
|
+
# :batch_size defaults to 20
|
33
|
+
# :verbose defaults to true
|
34
|
+
def delete_keys_in_temp_file(params = {})
|
35
|
+
batch_size = params.fetch(:batch_size, 20)
|
36
|
+
verbose = params.fetch(:verbose, true)
|
37
|
+
|
38
|
+
total_keys_count = 0
|
39
|
+
deleted_keys_count = 0
|
40
|
+
|
41
|
+
File.foreach(temp_file_path).each_slice(batch_size) do |keys_group|
|
42
|
+
total_keys_count = total_keys_count + keys_group.size
|
43
|
+
if removed_keys_count = remove_from_redis(strip_keys_group(keys_group))
|
44
|
+
puts "Successfully deleted key group of size #{keys_group.size}" if verbose
|
45
|
+
deleted_keys_count = deleted_keys_count + removed_keys_count
|
46
|
+
else
|
47
|
+
puts "Failed to delete: #{keys_group.inspect}" if verbose
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
{total_keys_count: total_keys_count, deleted_keys_count: deleted_keys_count}
|
52
|
+
end
|
53
|
+
|
54
|
+
def dump_matching_keys_to_temp_file(pattern)
|
55
|
+
resque_keys = redis.keys(pattern)
|
56
|
+
File.open(temp_file_path, "w+") do |file|
|
57
|
+
resque_keys.each do |key|
|
58
|
+
file.puts key
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def dump_and_delete(pattern, params = {})
|
64
|
+
verbose = params.fetch(:verbose, true)
|
65
|
+
delete_temp_file = params.fetch(:delete_temp_file, true)
|
66
|
+
|
67
|
+
puts "Dumping ..."
|
68
|
+
dump_matching_keys_to_temp_file(pattern)
|
69
|
+
puts "Dumping finished !"
|
70
|
+
puts "Deleting dumped keys ..."
|
71
|
+
cleanup_job_stats = delete_keys_in_temp_file(params)
|
72
|
+
|
73
|
+
File.delete(temp_file_path) if delete_temp_file
|
74
|
+
puts "Deleted #{cleanup_job_stats[:deleted_keys_count]} keys out of #{cleanup_job_stats[:total_keys_count]}"
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def strip_keys_group(keys_group = [])
|
80
|
+
keys_group.map{|element| element.strip}
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
gem.name = %q{redis-cleaner}
|
3
|
+
gem.version = "0.0.1"
|
4
|
+
gem.date = %q{2013-05-07}
|
5
|
+
gem.authors = ["Lloyd Meta"]
|
6
|
+
gem.email = ["lloydmeta@gmail.com"]
|
7
|
+
gem.homepage = "http://github.com/lloydmeta/redis-cleaner"
|
8
|
+
gem.description = %q{A Ruby gem for cleaning up of Redis keys by pattern matching. Handles huge amounts of keys. Can be used with any Redis client that responds to #del and #keys}
|
9
|
+
gem.summary = gem.description
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.require_paths = ["lib"]
|
15
|
+
|
16
|
+
gem.add_development_dependency 'rake'
|
17
|
+
gem.add_development_dependency 'rspec'
|
18
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'tempfile'
|
3
|
+
|
4
|
+
describe RedisCleaner do
|
5
|
+
|
6
|
+
let(:fake_keys){%w[key1 key2 key3 key4 key5 a_key1 a_key2]}
|
7
|
+
let(:redis_mock){
|
8
|
+
r = double("Redis mock", :keys => fake_keys)
|
9
|
+
r.stub(:del) do |arg|
|
10
|
+
arg.size
|
11
|
+
end
|
12
|
+
r
|
13
|
+
}
|
14
|
+
let(:temp_file){Tempfile.new('test_file')}
|
15
|
+
let(:temp_file_path){temp_file.path}
|
16
|
+
let(:redis_cleaner){RedisCleaner.new(redis_mock, temp_file_path)}
|
17
|
+
|
18
|
+
after(:all) do
|
19
|
+
temp_file.unlink
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "initialisation" do
|
23
|
+
|
24
|
+
it "should not error out" do
|
25
|
+
expect{
|
26
|
+
redis_cleaner = RedisCleaner.new(redis_mock, temp_file_path)
|
27
|
+
}.to_not raise_error
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
describe "#remove_from_redis" do
|
33
|
+
|
34
|
+
it "should receive key_group as an argument" do
|
35
|
+
redis_cleaner.should_receive(:remove_from_redis).with(fake_keys)
|
36
|
+
redis_cleaner.remove_from_redis(fake_keys)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should return the number of keys deleted if everything was deleted successfully" do
|
40
|
+
redis_cleaner.remove_from_redis(fake_keys).should eq(fake_keys.size)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should return false if the number of keys deleted does not equal the number of keys passed in" do
|
44
|
+
redis_mock.stub(:del) do |arg|
|
45
|
+
arg.size - 1
|
46
|
+
end
|
47
|
+
redis_cleaner.remove_from_redis(fake_keys).should be_false
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "#dump_matching_keys_to_temp_file" do
|
53
|
+
|
54
|
+
it "should receive a pattern as an argument" do
|
55
|
+
pattern = "asdf1234"
|
56
|
+
redis_cleaner.should_receive(:dump_matching_keys_to_temp_file).with(pattern)
|
57
|
+
redis_cleaner.dump_matching_keys_to_temp_file(pattern)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should call #keys on the redis object" do
|
61
|
+
pattern = "asdf1234"
|
62
|
+
redis_mock.should_receive(:keys).with(pattern)
|
63
|
+
redis_cleaner.dump_matching_keys_to_temp_file(pattern)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should dump the keys into the temp file" do
|
67
|
+
pattern = "asdf1234"
|
68
|
+
redis_cleaner.dump_matching_keys_to_temp_file(pattern)
|
69
|
+
temp_file_array = File.readlines(temp_file_path)
|
70
|
+
temp_file_array.zip(fake_keys).each do |(t, f)|
|
71
|
+
t.strip.should eq f
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#delete_keys_in_temp_file" do
|
78
|
+
|
79
|
+
before(:each) do
|
80
|
+
redis_cleaner.dump_matching_keys_to_temp_file("bleh")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should call #remove_from_redis" do
|
84
|
+
redis_cleaner.should_receive(:remove_from_redis)
|
85
|
+
redis_cleaner.delete_keys_in_temp_file(verbose: false)
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
describe "#dump_and_delete" do
|
91
|
+
|
92
|
+
let(:pattern){"lala"}
|
93
|
+
let(:fake_params){{asdf: 3, verbose: false}}
|
94
|
+
|
95
|
+
before(:each) do
|
96
|
+
redis_cleaner.stub(:delete_keys_in_temp_file).and_return({deleted_keys_count: fake_keys.size, total_keys_count: fake_keys.size})
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should call receive pattern and params as arguments" do
|
100
|
+
redis_cleaner.should_receive(:dump_and_delete).with(pattern, fake_params)
|
101
|
+
redis_cleaner.dump_and_delete(pattern, fake_params)
|
102
|
+
end
|
103
|
+
|
104
|
+
it "should call #dump_matching_keys_to_temp_file" do
|
105
|
+
redis_cleaner.should_receive(:dump_matching_keys_to_temp_file)
|
106
|
+
redis_cleaner.dump_and_delete(pattern, fake_params)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should call #delete_keys_in_temp_file" do
|
110
|
+
redis_cleaner.should_receive(:delete_keys_in_temp_file)
|
111
|
+
redis_cleaner.dump_and_delete(pattern, fake_params)
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: redis-cleaner
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Lloyd Meta
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rspec
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: ! 'A Ruby gem for cleaning up of Redis keys by pattern matching. Handles
|
47
|
+
huge amounts of keys. Can be used with any Redis client that responds to #del and
|
48
|
+
#keys'
|
49
|
+
email:
|
50
|
+
- lloydmeta@gmail.com
|
51
|
+
executables: []
|
52
|
+
extensions: []
|
53
|
+
extra_rdoc_files: []
|
54
|
+
files:
|
55
|
+
- .gitignore
|
56
|
+
- .rspec
|
57
|
+
- .travis.yml
|
58
|
+
- Gemfile
|
59
|
+
- README.md
|
60
|
+
- Rakefile
|
61
|
+
- lib/redis_cleaner.rb
|
62
|
+
- redis-cleaner.gemspec
|
63
|
+
- spec/redis-cleaner/redis_cleaner_spec.rb
|
64
|
+
- spec/spec_helper.rb
|
65
|
+
homepage: http://github.com/lloydmeta/redis-cleaner
|
66
|
+
licenses: []
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options: []
|
69
|
+
require_paths:
|
70
|
+
- lib
|
71
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ! '>='
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
78
|
+
none: false
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 1.8.25
|
86
|
+
signing_key:
|
87
|
+
specification_version: 3
|
88
|
+
summary: ! 'A Ruby gem for cleaning up of Redis keys by pattern matching. Handles
|
89
|
+
huge amounts of keys. Can be used with any Redis client that responds to #del and
|
90
|
+
#keys'
|
91
|
+
test_files:
|
92
|
+
- spec/redis-cleaner/redis_cleaner_spec.rb
|
93
|
+
- spec/spec_helper.rb
|