Module: Redis::KeyHash::ClassMethods

Defined in:
lib/redis/key_hash.rb

Instance Method Summary collapse

Instance Method Details

#all_in_one_slot!(*keys, namespace: nil, styles: DEFAULT_STYLES) ⇒ Object

Like all_in_one_slot?, mismatch raises Redis::ImpendingCrossSlotError.

all keys as per the redis-namespace gem before testing.

the styles, false if there is any doubt.

under all styles by virtue of having a single hash_tag.

keys have a different hash_tag hence will not provably have the same hash_slot

Parameters:

  • keys

    String keys to be tested

  • namespace (defaults to: nil)

    String or nil if non-nil, applied as a prefix to

  • styles (defaults to: DEFAULT_STYLES)

    Array of Symbols and/or Regexps as per hash_tag().

Returns:

  • true if all of keys will hash to the same slot in all of

  • true if all of keys provably have the same hash_slot



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/redis/key_hash.rb', line 98

def all_in_one_slot!(*keys, namespace: nil, styles: DEFAULT_STYLES)
  nkeys       = namespace ? keys.map { |key| "#{namespace}:#{key}" } : keys
  style2slot  = Hash.new
  problems    = []
  styles.each do |style|
    tags      = nkeys.map { |nkey| hash_tag(nkey,style: style) }.uniq
    next if tags.size <= 1
    problems << "style #{style} sees tags #{tags.join(',')}"
  end
  if 0 != problems.size
    err  = "CROSSSLOT"
    err += " namespace=#{namespace}"
    err += " keys=#{keys}"
    err += " problems=#{problems}"
    raise Redis::ImpendingCrossSlotError, err
  end
  true
end

#all_in_one_slot?(*keys, namespace: nil, styles: DEFAULT_STYLES) ⇒ Boolean

Tests whether all of keys will hash to the same slot in all specified sharding styles.

all keys as per the redis-namespace gem before testing.

all styles by virtue of having a single hash_tag, false otherwise.

Parameters:

  • keys

    String keys to be tested

  • namespace (defaults to: nil)

    String or nil if non-nil, applied as a prefix to

  • styles (defaults to: DEFAULT_STYLES)

    Array of Symbols and/or Regexps as per hash_tag().

Returns:

  • (Boolean)

    true if all of keys provably have the same hash_slot under



69
70
71
72
73
74
75
76
77
# File 'lib/redis/key_hash.rb', line 69

def all_in_one_slot?(*keys, namespace: nil, styles: DEFAULT_STYLES)
  begin
    all_in_one_slot!(*keys, namespace: namespace, styles: styles)
  rescue Redis::ImpendingCrossSlotError
    return false
  else
    return true
  end
end

#crc16(key) ⇒ Object

Computes the Redis crc16 for a given key, as per the reference implementation provided in redis.io/topics/cluster-spec.

This implementation is taken largely from that reference document, changed only slightly to port to Ruby.

compute a hash_key.

Parameters:

  • String

    key

  • non-negative

    Integer the crc16 which Redis will use to



172
173
174
175
176
177
178
# File 'lib/redis/key_hash.rb', line 172

def crc16(key)
  crc = 0
  key.each_char do |char|
    crc = ((crc << 8) & 0xFFFF) ^ CRC16TAB[((crc >> 8) ^ char.ord) & 0x00FF]
  end
  crc
end

#hash_slot(key, style: DEFAULT_STYLE) ⇒ Object

Computes the Redis hash_slot for a given key.

Uses :style as per hash_tag, but performs hashing as per RC only. We know through documentation and experimentation that RC uses crc16() and modulo 16384. We do not know what RLEC does, but until we have a better model we assume it is the same. This is probably a false assumption since the RLEC docs state that the number of shards can vary from cluster to cluster. But for many analyses, using the same hash as RC is still useful.

Parameters:

  • String

    key to be hashed

  • Symbol

    :rc or :rlec or Regexp which defines one capture group

  • non-negative

    Integer the hash which Redis will use to slot key



156
157
158
159
# File 'lib/redis/key_hash.rb', line 156

def hash_slot(key, style: DEFAULT_STYLE)
  tag = hash_tag(key, style: style)
  crc16(tag) % 16384
end

#hash_tag(key, style: DEFAULT_STYLE) ⇒ Object

Computes the hash tag for a given key under a given Redis clustering algorithm.

Parameters:

  • String

    key to be hashed

  • Symbol

    :rc or rlec or Regexp which defines one capture group

  • String

    the tag extracted from key as appropriate for :style.



126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/redis/key_hash.rb', line 126

def hash_tag(key, style: DEFAULT_STYLE)
  regexp = nil
  if KNOWN_STYLES.has_key?(style)
    regexp = KNOWN_STYLES[style] # some are predefined
  elsif style.is_a?(Regexp)
    regexp = style               # you can define your own
  end
  if !regexp
    raise ArgumentError, "bogus style #{style}"
  end
  match = regexp.match(key)
  return match ? match[1] : key
end