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 ADDED
@@ -0,0 +1,6 @@
1
+ .DS_Store
2
+ *.gem
3
+ .bundle
4
+ Gemfile.lock
5
+ pkg/*
6
+ .rvmrc
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development, :test do
6
+ gem 'rake'
7
+ gem 'rspec'
8
+ end
data/README.md ADDED
@@ -0,0 +1,57 @@
1
+ Redis-Cleaner [![Build Status](https://travis-ci.org/lloydmeta/redis-cleaner.png?branch=master)](https://travis-ci.org/lloydmeta/redis-cleaner) [![Code Climate](https://codeclimate.com/github/lloydmeta/redis-cleaner.png)](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,8 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new do |t|
6
+ end
7
+
8
+ task :default => :spec
@@ -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
@@ -0,0 +1,12 @@
1
+ require 'bundler/setup'
2
+ require 'rspec'
3
+ require 'rspec/mocks'
4
+ require 'redis_cleaner'
5
+
6
+ Dir[File.expand_path('../support/**/*', __FILE__)].each { |f| require f }
7
+
8
+ RSpec.configure do |config|
9
+
10
+ #nothing yet
11
+
12
+ end
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