redis_migrator 0.0.1 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +3 -0
- data/README.md +13 -1
- data/Rakefile +6 -0
- data/lib/redis_migrator/redis_helper.rb +5 -36
- data/lib/redis_migrator/redis_native_migrator.rb +27 -0
- data/lib/redis_migrator/redis_pipe_migrator.rb +66 -0
- data/lib/redis_migrator/redis_populator.rb +1 -1
- data/lib/redis_migrator.rb +19 -20
- data/migrator_benchmark.rb +1 -2
- data/redis_migrator.gemspec +8 -4
- data/spec/different_redis_type_migrator.rb +67 -0
- data/spec/pretested_migrator.rb +47 -0
- data/spec/redis_migrator_spec.rb +41 -0
- data/spec/redis_native_migrator_spec.rb +44 -0
- data/spec/redis_pipe_migrator_spec.rb +51 -0
- data/spec/shared_hosts_context.rb +10 -0
- data/spec/spec_helper.rb +9 -7
- metadata +85 -49
- data/spec/migrator_spec.rb +0 -63
- data/spec/mock_redis/lib/mock_redis/assertions.rb +0 -13
- data/spec/mock_redis/lib/mock_redis/database.rb +0 -432
- data/spec/mock_redis/lib/mock_redis/distributed.rb +0 -6
- data/spec/mock_redis/lib/mock_redis/exceptions.rb +0 -3
- data/spec/mock_redis/lib/mock_redis/expire_wrapper.rb +0 -25
- data/spec/mock_redis/lib/mock_redis/hash_methods.rb +0 -118
- data/spec/mock_redis/lib/mock_redis/list_methods.rb +0 -187
- data/spec/mock_redis/lib/mock_redis/multi_db_wrapper.rb +0 -86
- data/spec/mock_redis/lib/mock_redis/set_methods.rb +0 -126
- data/spec/mock_redis/lib/mock_redis/string_methods.rb +0 -203
- data/spec/mock_redis/lib/mock_redis/transaction_wrapper.rb +0 -80
- data/spec/mock_redis/lib/mock_redis/undef_redis_methods.rb +0 -11
- data/spec/mock_redis/lib/mock_redis/utility_methods.rb +0 -25
- data/spec/mock_redis/lib/mock_redis/version.rb +0 -3
- data/spec/mock_redis/lib/mock_redis/zset.rb +0 -110
- data/spec/mock_redis/lib/mock_redis/zset_methods.rb +0 -210
- data/spec/mock_redis/lib/mock_redis.rb +0 -119
- data/spec/redis_helper_spec.rb +0 -58
@@ -1,25 +0,0 @@
|
|
1
|
-
require 'mock_redis/undef_redis_methods'
|
2
|
-
|
3
|
-
class MockRedis
|
4
|
-
class ExpireWrapper
|
5
|
-
include UndefRedisMethods
|
6
|
-
|
7
|
-
def respond_to?(method, include_private=false)
|
8
|
-
super || @db.respond_to?(method)
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize(db)
|
12
|
-
@db = db
|
13
|
-
end
|
14
|
-
|
15
|
-
def method_missing(method, *args)
|
16
|
-
@db.expire_keys
|
17
|
-
@db.send(method, *args)
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize_copy(source)
|
21
|
-
super
|
22
|
-
@db = @db.clone
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,118 +0,0 @@
|
|
1
|
-
require 'mock_redis/assertions'
|
2
|
-
require 'mock_redis/utility_methods'
|
3
|
-
|
4
|
-
class MockRedis
|
5
|
-
module HashMethods
|
6
|
-
include Assertions
|
7
|
-
include UtilityMethods
|
8
|
-
|
9
|
-
def hdel(key, field)
|
10
|
-
with_hash_at(key) do |hash|
|
11
|
-
hash.delete(field.to_s) ? 1 : 0
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
def hexists(key, field)
|
16
|
-
with_hash_at(key) {|h| h.has_key?(field.to_s)}
|
17
|
-
end
|
18
|
-
|
19
|
-
def hget(key, field)
|
20
|
-
with_hash_at(key) {|h| h[field.to_s]}
|
21
|
-
end
|
22
|
-
|
23
|
-
def hgetall(key)
|
24
|
-
with_hash_at(key) {|h| h}
|
25
|
-
end
|
26
|
-
|
27
|
-
def hincrby(key, field, increment)
|
28
|
-
with_hash_at(key) do |hash|
|
29
|
-
field = field.to_s
|
30
|
-
unless can_incr?(data[key][field])
|
31
|
-
raise RuntimeError, "ERR hash value is not an integer"
|
32
|
-
end
|
33
|
-
unless looks_like_integer?(increment.to_s)
|
34
|
-
raise RuntimeError, "ERR value is not an integer or out of range"
|
35
|
-
end
|
36
|
-
|
37
|
-
new_value = (hash[field] || "0").to_i + increment.to_i
|
38
|
-
hash[field] = new_value.to_s
|
39
|
-
new_value
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def hkeys(key)
|
44
|
-
with_hash_at(key, &:keys)
|
45
|
-
end
|
46
|
-
|
47
|
-
def hlen(key)
|
48
|
-
hkeys(key).length
|
49
|
-
end
|
50
|
-
|
51
|
-
def hmget(key, *fields)
|
52
|
-
assert_has_args(fields, 'hmget')
|
53
|
-
fields.map{|f| hget(key, f)}
|
54
|
-
end
|
55
|
-
|
56
|
-
def mapped_hmget(key, *fields)
|
57
|
-
reply = hmget(key, *fields)
|
58
|
-
Hash[*fields.zip(reply).flatten]
|
59
|
-
end
|
60
|
-
|
61
|
-
def hmset(key, *kvpairs)
|
62
|
-
assert_has_args(kvpairs, 'hmset')
|
63
|
-
if kvpairs.length.odd?
|
64
|
-
raise RuntimeError, "ERR wrong number of arguments for HMSET"
|
65
|
-
end
|
66
|
-
|
67
|
-
kvpairs.each_slice(2) do |(k,v)|
|
68
|
-
hset(key, k, v)
|
69
|
-
end
|
70
|
-
'OK'
|
71
|
-
end
|
72
|
-
|
73
|
-
def mapped_hmset(key, hash)
|
74
|
-
kvpairs = hash.to_a.flatten
|
75
|
-
assert_has_args(kvpairs, 'hmset')
|
76
|
-
if kvpairs.length.odd?
|
77
|
-
raise RuntimeError, "ERR wrong number of arguments for 'hmset' command"
|
78
|
-
end
|
79
|
-
|
80
|
-
hmset(key, *kvpairs)
|
81
|
-
end
|
82
|
-
|
83
|
-
def hset(key, field, value)
|
84
|
-
with_hash_at(key) {|h| h[field.to_s] = value.to_s}
|
85
|
-
true
|
86
|
-
end
|
87
|
-
|
88
|
-
def hsetnx(key, field, value)
|
89
|
-
if hget(key, field)
|
90
|
-
false
|
91
|
-
else
|
92
|
-
hset(key, field, value)
|
93
|
-
true
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def hvals(key)
|
98
|
-
with_hash_at(key, &:values)
|
99
|
-
end
|
100
|
-
|
101
|
-
private
|
102
|
-
|
103
|
-
def with_hash_at(key, &blk)
|
104
|
-
with_thing_at(key, :assert_hashy, proc {{}}, &blk)
|
105
|
-
end
|
106
|
-
|
107
|
-
def hashy?(key)
|
108
|
-
data[key].nil? || data[key].kind_of?(Hash)
|
109
|
-
end
|
110
|
-
|
111
|
-
def assert_hashy(key)
|
112
|
-
unless hashy?(key)
|
113
|
-
raise RuntimeError, "ERR Operation against a key holding the wrong kind of value"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
end
|
118
|
-
end
|
@@ -1,187 +0,0 @@
|
|
1
|
-
require 'mock_redis/assertions'
|
2
|
-
require 'mock_redis/utility_methods'
|
3
|
-
|
4
|
-
class MockRedis
|
5
|
-
module ListMethods
|
6
|
-
include Assertions
|
7
|
-
include UtilityMethods
|
8
|
-
|
9
|
-
def blpop(*args)
|
10
|
-
lists, timeout = extract_timeout(args)
|
11
|
-
nonempty_list = first_nonempty_list(lists)
|
12
|
-
|
13
|
-
if nonempty_list
|
14
|
-
[nonempty_list, lpop(nonempty_list)]
|
15
|
-
elsif timeout > 0
|
16
|
-
nil
|
17
|
-
else
|
18
|
-
raise MockRedis::WouldBlock, "Can't block forever"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def brpop(*args)
|
23
|
-
lists, timeout = extract_timeout(args)
|
24
|
-
nonempty_list = first_nonempty_list(lists)
|
25
|
-
|
26
|
-
if nonempty_list
|
27
|
-
[nonempty_list, rpop(nonempty_list)]
|
28
|
-
elsif timeout > 0
|
29
|
-
nil
|
30
|
-
else
|
31
|
-
raise MockRedis::WouldBlock, "Can't block forever"
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def brpoplpush(source, destination, timeout)
|
36
|
-
assert_valid_timeout(timeout)
|
37
|
-
|
38
|
-
if llen(source) > 0
|
39
|
-
rpoplpush(source, destination)
|
40
|
-
elsif timeout > 0
|
41
|
-
nil
|
42
|
-
else
|
43
|
-
raise MockRedis::WouldBlock, "Can't block forever"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def lindex(key, index)
|
48
|
-
with_list_at(key) {|l| l[index]}
|
49
|
-
end
|
50
|
-
|
51
|
-
def linsert(key, position, pivot, value)
|
52
|
-
unless %w[before after].include?(position.to_s)
|
53
|
-
raise RuntimeError, "ERR syntax error"
|
54
|
-
end
|
55
|
-
|
56
|
-
assert_listy(key)
|
57
|
-
return 0 unless data[key]
|
58
|
-
|
59
|
-
pivot_position = (0..llen(key) - 1).find do |i|
|
60
|
-
data[key][i] == pivot.to_s
|
61
|
-
end
|
62
|
-
|
63
|
-
return -1 unless pivot_position
|
64
|
-
|
65
|
-
insertion_index = if position.to_s == 'before'
|
66
|
-
pivot_position
|
67
|
-
else
|
68
|
-
pivot_position + 1
|
69
|
-
end
|
70
|
-
|
71
|
-
data[key].insert(insertion_index, value.to_s)
|
72
|
-
llen(key)
|
73
|
-
end
|
74
|
-
|
75
|
-
def llen(key)
|
76
|
-
with_list_at(key, &:length)
|
77
|
-
end
|
78
|
-
|
79
|
-
def lpop(key)
|
80
|
-
with_list_at(key, &:shift)
|
81
|
-
end
|
82
|
-
|
83
|
-
def lpush(key, value)
|
84
|
-
with_list_at(key) {|l| l.unshift(value.to_s)}
|
85
|
-
llen(key)
|
86
|
-
end
|
87
|
-
|
88
|
-
def lpushx(key, value)
|
89
|
-
assert_listy(key)
|
90
|
-
return 0 unless list_at?(key)
|
91
|
-
lpush(key, value)
|
92
|
-
end
|
93
|
-
|
94
|
-
def lrange(key, start, stop)
|
95
|
-
with_list_at(key) {|l| l[start..stop]}
|
96
|
-
end
|
97
|
-
|
98
|
-
def lrem(key, count, value)
|
99
|
-
count = count.to_i
|
100
|
-
value = value.to_s
|
101
|
-
|
102
|
-
with_list_at(key) do |list|
|
103
|
-
indices_with_value = (0..(llen(key) - 1)).find_all do |i|
|
104
|
-
list[i] == value
|
105
|
-
end
|
106
|
-
|
107
|
-
indices_to_delete = if count == 0
|
108
|
-
indices_with_value.reverse
|
109
|
-
elsif count > 0
|
110
|
-
indices_with_value.take(count).reverse
|
111
|
-
else
|
112
|
-
indices_with_value.reverse.take(-count)
|
113
|
-
end
|
114
|
-
|
115
|
-
indices_to_delete.each {|i| list.delete_at(i)}.length
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def lset(key, index, value)
|
120
|
-
assert_listy(key)
|
121
|
-
|
122
|
-
unless list_at?(key)
|
123
|
-
raise RuntimeError, "ERR no such key"
|
124
|
-
end
|
125
|
-
|
126
|
-
unless (0...llen(key)).include?(index)
|
127
|
-
raise RuntimeError, "ERR index out of range"
|
128
|
-
end
|
129
|
-
|
130
|
-
data[key][index] = value.to_s
|
131
|
-
'OK'
|
132
|
-
end
|
133
|
-
|
134
|
-
def ltrim(key, start, stop)
|
135
|
-
with_list_at(key) do |list|
|
136
|
-
list.replace(list[start..stop] || []) if list
|
137
|
-
'OK'
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
def rpop(key)
|
142
|
-
with_list_at(key) {|list| list.pop if list}
|
143
|
-
end
|
144
|
-
|
145
|
-
def rpoplpush(source, destination)
|
146
|
-
value = rpop(source)
|
147
|
-
lpush(destination, value)
|
148
|
-
value
|
149
|
-
end
|
150
|
-
|
151
|
-
def rpush(key, value)
|
152
|
-
with_list_at(key) {|l| l.push(value.to_s)}
|
153
|
-
llen(key)
|
154
|
-
end
|
155
|
-
|
156
|
-
def rpushx(key, value)
|
157
|
-
assert_listy(key)
|
158
|
-
return 0 unless list_at?(key)
|
159
|
-
rpush(key, value)
|
160
|
-
end
|
161
|
-
|
162
|
-
private
|
163
|
-
def list_at?(key)
|
164
|
-
data[key] && listy?(key)
|
165
|
-
end
|
166
|
-
|
167
|
-
def with_list_at(key, &blk)
|
168
|
-
with_thing_at(key, :assert_listy, proc {[]}, &blk)
|
169
|
-
end
|
170
|
-
|
171
|
-
def listy?(key)
|
172
|
-
data[key].nil? || data[key].kind_of?(Array)
|
173
|
-
end
|
174
|
-
|
175
|
-
def assert_listy(key)
|
176
|
-
unless listy?(key)
|
177
|
-
# Not the most helpful error, but it's what redis-rb barfs up
|
178
|
-
raise RuntimeError, "ERR Operation against a key holding the wrong kind of value"
|
179
|
-
end
|
180
|
-
end
|
181
|
-
|
182
|
-
def first_nonempty_list(keys)
|
183
|
-
keys.find{|k| llen(k) > 0}
|
184
|
-
end
|
185
|
-
|
186
|
-
end
|
187
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
require 'mock_redis/undef_redis_methods'
|
2
|
-
|
3
|
-
class MockRedis
|
4
|
-
class MultiDbWrapper
|
5
|
-
include UndefRedisMethods
|
6
|
-
|
7
|
-
def initialize(db)
|
8
|
-
@db_index = 0
|
9
|
-
|
10
|
-
@prototype_db = db.clone
|
11
|
-
|
12
|
-
@databases = Hash.new {|h,k| h[k] = @prototype_db.clone}
|
13
|
-
@databases[@db_index] = db
|
14
|
-
end
|
15
|
-
|
16
|
-
def respond_to?(method, include_private=false)
|
17
|
-
super || current_db.respond_to?(method, include_private)
|
18
|
-
end
|
19
|
-
|
20
|
-
def method_missing(method, *args)
|
21
|
-
current_db.send(method, *args)
|
22
|
-
end
|
23
|
-
|
24
|
-
def initialize_copy(source)
|
25
|
-
super
|
26
|
-
@databases = @databases.clone
|
27
|
-
@databases.keys.each do |k|
|
28
|
-
@databases[k] = @databases[k].clone
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
# Redis commands
|
33
|
-
def flushall
|
34
|
-
@databases.values.each(&:flushdb)
|
35
|
-
'OK'
|
36
|
-
end
|
37
|
-
|
38
|
-
def move(key, db_index)
|
39
|
-
src = current_db
|
40
|
-
dest = db(db_index)
|
41
|
-
|
42
|
-
if !src.exists(key) || dest.exists(key)
|
43
|
-
false
|
44
|
-
else
|
45
|
-
case current_db.type(key)
|
46
|
-
when 'hash'
|
47
|
-
dest.hmset(key, *(src.hgetall(key).map{|k,v| [k,v]}.flatten))
|
48
|
-
when 'list'
|
49
|
-
while value = src.rpop(key)
|
50
|
-
dest.lpush(key, value)
|
51
|
-
end
|
52
|
-
when 'set'
|
53
|
-
while value = src.spop(key)
|
54
|
-
dest.sadd(key, value)
|
55
|
-
end
|
56
|
-
when 'string'
|
57
|
-
dest.set(key, src.get(key))
|
58
|
-
when 'zset'
|
59
|
-
src.zrange(key, 0, -1, :with_scores => true).each_slice(2) do |(m,s)|
|
60
|
-
dest.zadd(key, s, m)
|
61
|
-
end
|
62
|
-
else
|
63
|
-
raise ArgumentError,
|
64
|
-
"Can't move a key of type #{current_db.type(key).inspect}"
|
65
|
-
end
|
66
|
-
|
67
|
-
src.del(key)
|
68
|
-
true
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def select(db_index)
|
73
|
-
@db_index = db_index.to_i
|
74
|
-
'OK'
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
def current_db
|
79
|
-
@databases[@db_index]
|
80
|
-
end
|
81
|
-
|
82
|
-
def db(index)
|
83
|
-
@databases[index]
|
84
|
-
end
|
85
|
-
end
|
86
|
-
end
|
@@ -1,126 +0,0 @@
|
|
1
|
-
require 'mock_redis/assertions'
|
2
|
-
require 'mock_redis/utility_methods'
|
3
|
-
|
4
|
-
class MockRedis
|
5
|
-
module SetMethods
|
6
|
-
include Assertions
|
7
|
-
include UtilityMethods
|
8
|
-
|
9
|
-
def sadd(key, member)
|
10
|
-
with_set_at(key) {|s| !!s.add?(member.to_s)}
|
11
|
-
end
|
12
|
-
|
13
|
-
def scard(key)
|
14
|
-
with_set_at(key) {|s| s.length}
|
15
|
-
end
|
16
|
-
|
17
|
-
def sdiff(*keys)
|
18
|
-
assert_has_args(keys, 'sdiff')
|
19
|
-
with_sets_at(*keys) {|*sets| sets.reduce(&:-)}.to_a
|
20
|
-
end
|
21
|
-
|
22
|
-
def sdiffstore(destination, *keys)
|
23
|
-
assert_has_args(keys, 'sdiffstore')
|
24
|
-
with_set_at(destination) do |set|
|
25
|
-
set.replace(sdiff(*keys))
|
26
|
-
end
|
27
|
-
scard(destination)
|
28
|
-
end
|
29
|
-
|
30
|
-
def sinter(*keys)
|
31
|
-
assert_has_args(keys, 'sinter')
|
32
|
-
|
33
|
-
with_sets_at(*keys) do |*sets|
|
34
|
-
sets.reduce(&:&).to_a
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def sinterstore(destination, *keys)
|
39
|
-
assert_has_args(keys, 'sinterstore')
|
40
|
-
with_set_at(destination) do |set|
|
41
|
-
set.replace(sinter(*keys))
|
42
|
-
end
|
43
|
-
scard(destination)
|
44
|
-
end
|
45
|
-
|
46
|
-
def sismember(key, member)
|
47
|
-
with_set_at(key) {|s| s.include?(member.to_s)}
|
48
|
-
end
|
49
|
-
|
50
|
-
def smembers(key)
|
51
|
-
with_set_at(key, &:to_a)
|
52
|
-
end
|
53
|
-
|
54
|
-
def smove(src, dest, member)
|
55
|
-
member = member.to_s
|
56
|
-
|
57
|
-
with_sets_at(src, dest) do |src_set, dest_set|
|
58
|
-
if src_set.delete?(member)
|
59
|
-
dest_set.add(member)
|
60
|
-
true
|
61
|
-
else
|
62
|
-
false
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def spop(key)
|
68
|
-
with_set_at(key) do |set|
|
69
|
-
member = set.first
|
70
|
-
set.delete(member)
|
71
|
-
member
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
def srandmember(key)
|
76
|
-
members = with_set_at(key, &:to_a)
|
77
|
-
members[rand(members.length)]
|
78
|
-
end
|
79
|
-
|
80
|
-
def srem(key, member)
|
81
|
-
with_set_at(key) {|s| !!s.delete?(member.to_s)}
|
82
|
-
end
|
83
|
-
|
84
|
-
def sunion(*keys)
|
85
|
-
assert_has_args(keys, 'sunion')
|
86
|
-
with_sets_at(*keys) {|*sets| sets.reduce(&:+).to_a}
|
87
|
-
end
|
88
|
-
|
89
|
-
def sunionstore(destination, *keys)
|
90
|
-
assert_has_args(keys, 'sunionstore')
|
91
|
-
with_set_at(destination) do |dest_set|
|
92
|
-
dest_set.replace(sunion(*keys))
|
93
|
-
end
|
94
|
-
scard(destination)
|
95
|
-
end
|
96
|
-
|
97
|
-
private
|
98
|
-
def with_set_at(key, &blk)
|
99
|
-
with_thing_at(key, :assert_sety, proc {Set.new}, &blk)
|
100
|
-
end
|
101
|
-
|
102
|
-
def with_sets_at(*keys, &blk)
|
103
|
-
if keys.length == 1
|
104
|
-
with_set_at(keys.first, &blk)
|
105
|
-
else
|
106
|
-
with_set_at(keys.first) do |set|
|
107
|
-
with_sets_at(*(keys[1..-1])) do |*sets|
|
108
|
-
blk.call(*([set] + sets))
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
def sety?(key)
|
115
|
-
data[key].nil? || data[key].kind_of?(Set)
|
116
|
-
end
|
117
|
-
|
118
|
-
def assert_sety(key)
|
119
|
-
unless sety?(key)
|
120
|
-
# Not the most helpful error, but it's what redis-rb barfs up
|
121
|
-
raise RuntimeError, "ERR Operation against a key holding the wrong kind of value"
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
end
|
126
|
-
end
|