Class: RedCluster

Inherits:
Object
  • Object
show all
Defined in:
lib/red_cluster.rb,
lib/replica_set.rb,
lib/replica_set.rb

Defined Under Namespace

Classes: NoMaster, ReplicaSet

Constant Summary collapse

SINGLE_KEY_KEY_OPS =
%W{del exists expire expireat move persists ttl type}.map(&:to_sym)
STRING_OPS =
%W{append decr decrby get getbit getrange getset incr incrby mget mset msetnx set setbit setex setnx setrange strlen}.map(&:to_sym)
HASH_OPS =
%W{hdel hexists hget hgetall hincrby hkeys hlen hmget hmset hset hsetnx hvals}.map(&:to_sym)
SINGLE_KEY_LIST_OPS =
%W{blpop brpop lindex linsert llen lpop lpush lpushx lrange lrem lset ltrim rpop rpush rpushx}.map(&:to_sym)
SINGLE_KEY_SET_OPS =
%W{sadd scard sismember smembers spop srandmember srem}.map(&:to_sym)
SINGLE_KEY_SORTED_SET_OPS =
%W{zadd zcard zcount zincrby zrange zrangebyscore zrank zrem zremrangebyrank zremrangebyscore zrevrange zrevrangebyscore zrevrank zscore}.map(&:to_sym)
SINGLE_KEY_OPS =
SINGLE_KEY_KEY_OPS + STRING_OPS + HASH_OPS + SINGLE_KEY_LIST_OPS + SINGLE_KEY_SET_OPS + SINGLE_KEY_SORTED_SET_OPS

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(replica_sets = []) ⇒ RedCluster

Returns a new instance of RedCluster.



9
10
11
# File 'lib/red_cluster.rb', line 9

def initialize(replica_sets = [])
  @replica_sets = replica_sets.map { |replica_set| ReplicaSet.new(self, replica_set) }
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method, *args) ⇒ Object



133
134
135
136
137
138
139
140
141
# File 'lib/red_cluster.rb', line 133

def method_missing(method, *args)
  if SINGLE_KEY_OPS.include?(method.to_sym)
    key = args.first
    replica_set = replica_set_for_key key
    replica_set.send method, *args
  else
    raise "Unsupported operation: #{method}"
  end
end

Instance Attribute Details

#replica_setsObject (readonly)

Returns the value of attribute replica_sets.



7
8
9
# File 'lib/red_cluster.rb', line 7

def replica_sets
  @replica_sets
end

Instance Method Details

#bgsaveObject



31
# File 'lib/red_cluster.rb', line 31

def bgsave; @replica_sets.each(&:bgsave); "Background saving started"; end

#brpoplpush(src_list, target_list, timeout) ⇒ Object



81
82
83
84
85
86
# File 'lib/red_cluster.rb', line 81

def brpoplpush(src_list, target_list, timeout)
  val = brpop src_list, timeout
  return unless val
  lpush target_list, val
  val
end

#config(cmd, *args) ⇒ Object



37
38
39
40
41
42
43
44
# File 'lib/red_cluster.rb', line 37

def config(cmd, *args)
  if cmd == :get
    @replica_sets.inject({}) { |result, replica_set| result.merge(replica_set.config(:get, *args)) }
  else
    @replica_sets.each { |replica_set| replica_set.config(cmd, *args) }
    "OK"
  end
end

#echo(msg) ⇒ Object



32
# File 'lib/red_cluster.rb', line 32

def echo(msg); @replica_sets.each {|replica_set| replica_set.echo(msg) }; msg; end

#execObject

Transaction Ops



47
48
49
50
51
52
53
54
55
# File 'lib/red_cluster.rb', line 47

def exec
  @multi_count = nil
  exec_results = @replica_sets.map(&:exec)
  #We'll get back a deeply nested array of arrays of the kind
  #[[3, 30], [10, 1]], [1, "OK"]] - where the first element in each leaf array is the RANK and the second is the result
  #We need to return back the results sorted by rank. So in the above case it would be
  #["OK", 30, 1]. Ruby's full-LISP toolbox to the rescue
  Hash[*exec_results.flatten].sort.map(&:last)
end

#keys(pattern) ⇒ Object



34
# File 'lib/red_cluster.rb', line 34

def keys(pattern); @replica_sets.map { |replica_set| replica_set.keys pattern }.flatten; end

#lastsaveObject



35
# File 'lib/red_cluster.rb', line 35

def lastsave; @replica_sets.map(&:lastsave).min; end

#load_aof_file(file_path) ⇒ Object



143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/red_cluster.rb', line 143

def load_aof_file(file_path)
  aof_file = File.read file_path
  commands = aof_file.split /^\*/
  commands.each do |cmd|
    split_cmd = cmd.split("\r\n")[1..-1]
    next unless split_cmd
    split_cmd.reject! { |cmd| cmd =~ /^\$/ }
    redis_cmd, redis_key, redis_args = split_cmd[0], split_cmd[1], split_cmd[2..-1]
    crc32_of_key = Zlib.crc32(redis_key).abs
    replica_set = @replica_sets[crc32_of_key % @replica_sets.size]
    replica_set.master.send redis_cmd, redis_key, *redis_args
  end
end

#pingObject



30
# File 'lib/red_cluster.rb', line 30

def ping; @replica_sets.each(&:ping); "PONG"; end

#randomkeyObject

Key Ops



58
59
60
61
62
63
# File 'lib/red_cluster.rb', line 58

def randomkey
  replica_sets_with_keys_in_them  = @replica_sets.select { |replica_set| replica_set.randomkey != nil }
  idx = (rand * replica_sets_with_keys_in_them.count).to_i
  rand_replica_set = replica_sets_with_keys_in_them[idx]
  rand_replica_set && rand_replica_set.randomkey
end

#rename(key, new_key) ⇒ Object

Raises:

  • (RuntimeError)


65
66
67
68
69
70
71
# File 'lib/red_cluster.rb', line 65

def rename(key, new_key)
  raise RuntimeError, "ERR source and destination objects are the same" if key == new_key
  raise RuntimeError, "ERR no such key" unless exists(key)
  val = get key
  del key
  set new_key, val
end

#rpoplpush(src_list, target_list) ⇒ Object

List Ops



74
75
76
77
78
79
# File 'lib/red_cluster.rb', line 74

def rpoplpush(src_list, target_list)
  val = rpop src_list
  return unless val
  lpush target_list, val
  val
end

#sdiff(*sets) ⇒ Object



99
100
101
# File 'lib/red_cluster.rb', line 99

def sdiff(*sets)
  perform_set_strategy :difference, *sets
end

#sdiffstore(destination, *sets) ⇒ Object



119
120
121
# File 'lib/red_cluster.rb', line 119

def sdiffstore(destination, *sets)
  perform_store_strategy :sdiff, destination, *sets
end

#select(db) ⇒ Object

Server Ops



22
# File 'lib/red_cluster.rb', line 22

def select(db); @replica_sets.each {|replica_set| replica_set.select(db) }; "OK"; end

#sinter(*sets) ⇒ Object



103
104
105
# File 'lib/red_cluster.rb', line 103

def sinter(*sets)
  perform_set_strategy :intersection, *sets
end

#sinterstore(destination, *sets) ⇒ Object



111
112
113
# File 'lib/red_cluster.rb', line 111

def sinterstore(destination, *sets)
  perform_store_strategy :sinter, destination, *sets
end

#smove(src, destination, member) ⇒ Object

Set Ops



89
90
91
92
93
94
95
96
97
# File 'lib/red_cluster.rb', line 89

def smove(src, destination, member)
  if sismember src, member
    sadd destination, member
    srem src, member
    true
  else
    false
  end
end

#sunion(*sets) ⇒ Object



107
108
109
# File 'lib/red_cluster.rb', line 107

def sunion(*sets)
  perform_set_strategy :union, *sets
end

#sunionstore(destination, *sets) ⇒ Object



115
116
117
# File 'lib/red_cluster.rb', line 115

def sunionstore(destination, *sets)
  perform_store_strategy :sunion, destination, *sets
end

#zinterstore(destination, input_sets, options = {}) ⇒ Object

Sorted Set Ops



124
125
126
# File 'lib/red_cluster.rb', line 124

def zinterstore(destination, input_sets, options = {})
  perform_sorted_set_store_strategy :intersection, destination, input_sets, options
end

#zunionstore(destination, input_sets, options = {}) ⇒ Object



128
129
130
# File 'lib/red_cluster.rb', line 128

def zunionstore(destination, input_sets, options = {})
  perform_sorted_set_store_strategy :union, destination, input_sets, options
end