Module: Redis::KeyHash::ClassMethods
- Defined in:
- lib/redis/key_hash.rb
Instance Method Summary collapse
-
#all_in_one_slot!(*keys, namespace: nil, styles: DEFAULT_STYLES) ⇒ Object
Like all_in_one_slot?, mismatch raises Redis::ImpendingCrossSlotError.
-
#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.
-
#crc16(key) ⇒ Object
Computes the Redis crc16 for a given key, as per the reference implementation provided in redis.io/topics/cluster-spec.
-
#hash_slot(key, style: DEFAULT_STYLE) ⇒ Object
Computes the Redis hash_slot for a given key.
-
#hash_tag(key, style: DEFAULT_STYLE) ⇒ Object
Computes the hash tag for a given key under a given Redis clustering algorithm.
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
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/redis/key_hash.rb', line 78 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| = nkeys.map { |nkey| hash_tag(nkey,style: style) }.uniq next if .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.
49 50 51 52 53 54 55 56 57 |
# File 'lib/redis/key_hash.rb', line 49 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.
169 170 171 172 173 174 175 |
# File 'lib/redis/key_hash.rb', line 169 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.
153 154 155 156 |
# File 'lib/redis/key_hash.rb', line 153 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.
The :style => :rc implementation matches Redis Cluster exactly and is taken largely from the reference example provided in redis.io/topics/cluster-spec.
The :style => :rlec implementation is partly speculative. It is mostly interpreted from the default RedisLabs Enterprise Cluster document redislabs.com/redis-enterprise-documentation/concepts-architecture/architecture/database-clustering/ plus our experience at ProsperWorks that, out of the box, RLEC uses the last {}-expr, not the first as in RC, from :rc (which we would not care about), they are also structually different (which can cause bugs unless we take special care). :rc uses the first {}-expr in the, :rlec uses the last {}-expr.
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 |
# File 'lib/redis/key_hash.rb', line 120 def hash_tag(key, style: DEFAULT_STYLE) regexp = nil case style when :rc regexp = /{([^}]*)}/ # RC uses the first {}-expr when :rlec regexp = /.*\{(.*)\}.*/ # RLEC default per docs, uses last {}-expr when 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 |