Module: Gitlab::Instrumentation::RedisClusterValidator

Defined in:
lib/gitlab/instrumentation/redis_cluster_validator.rb

Constant Summary collapse

REDIS_COMMANDS =

Generate with:

Gitlab::Redis::Cache

.with { |redis| redis.call('COMMAND') }
.select { |cmd| cmd[3] != 0 }
.map { |cmd| [
              cmd[0].upcase,
              { first: cmd[3], last: cmd[4], step: cmd[5], single_key: cmd[3] == cmd[4] }
             ]
}
.sort_by(&:first)
.to_h
{
  "APPEND" => { first: 1, last: 1, step: 1, single_key: true },
  "BITCOUNT" => { first: 1, last: 1, step: 1, single_key: true },
  "BITFIELD" => { first: 1, last: 1, step: 1, single_key: true },
  "BITFIELD_RO" => { first: 1, last: 1, step: 1, single_key: true },
  "BITOP" => { first: 2, last: -1, step: 1, single_key: false },
  "BITPOS" => { first: 1, last: 1, step: 1, single_key: true },
  "BLMOVE" => { first: 1, last: 2, step: 1, single_key: false },
  "BLPOP" => { first: 1, last: -2, step: 1, single_key: false },
  "BRPOP" => { first: 1, last: -2, step: 1, single_key: false },
  "BRPOPLPUSH" => { first: 1, last: 2, step: 1, single_key: false },
  "BZPOPMAX" => { first: 1, last: -2, step: 1, single_key: false },
  "BZPOPMIN" => { first: 1, last: -2, step: 1, single_key: false },
  "COPY" => { first: 1, last: 2, step: 1, single_key: false },
  "DECR" => { first: 1, last: 1, step: 1, single_key: true },
  "DECRBY" => { first: 1, last: 1, step: 1, single_key: true },
  "DEL" => { first: 1, last: -1, step: 1, single_key: false },
  "DUMP" => { first: 1, last: 1, step: 1, single_key: true },
  "EXISTS" => { first: 1, last: -1, step: 1, single_key: false },
  "EXPIRE" => { first: 1, last: 1, step: 1, single_key: true },
  "EXPIREAT" => { first: 1, last: 1, step: 1, single_key: true },
  "GEOADD" => { first: 1, last: 1, step: 1, single_key: true },
  "GEODIST" => { first: 1, last: 1, step: 1, single_key: true },
  "GEOHASH" => { first: 1, last: 1, step: 1, single_key: true },
  "GEOPOS" => { first: 1, last: 1, step: 1, single_key: true },
  "GEORADIUS" => { first: 1, last: 1, step: 1, single_key: true },
  "GEORADIUSBYMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
  "GEORADIUSBYMEMBER_RO" => { first: 1, last: 1, step: 1, single_key: true },
  "GEORADIUS_RO" => { first: 1, last: 1, step: 1, single_key: true },
  "GEOSEARCH" => { first: 1, last: 1, step: 1, single_key: true },
  "GEOSEARCHSTORE" => { first: 1, last: 2, step: 1, single_key: false },
  "GET" => { first: 1, last: 1, step: 1, single_key: true },
  "GETBIT" => { first: 1, last: 1, step: 1, single_key: true },
  "GETDEL" => { first: 1, last: 1, step: 1, single_key: true },
  "GETEX" => { first: 1, last: 1, step: 1, single_key: true },
  "GETRANGE" => { first: 1, last: 1, step: 1, single_key: true },
  "GETSET" => { first: 1, last: 1, step: 1, single_key: true },
  "HDEL" => { first: 1, last: 1, step: 1, single_key: true },
  "HEXISTS" => { first: 1, last: 1, step: 1, single_key: true },
  "HGET" => { first: 1, last: 1, step: 1, single_key: true },
  "HGETALL" => { first: 1, last: 1, step: 1, single_key: true },
  "HINCRBY" => { first: 1, last: 1, step: 1, single_key: true },
  "HINCRBYFLOAT" => { first: 1, last: 1, step: 1, single_key: true },
  "HKEYS" => { first: 1, last: 1, step: 1, single_key: true },
  "HLEN" => { first: 1, last: 1, step: 1, single_key: true },
  "HMGET" => { first: 1, last: 1, step: 1, single_key: true },
  "HMSET" => { first: 1, last: 1, step: 1, single_key: true },
  "HRANDFIELD" => { first: 1, last: 1, step: 1, single_key: true },
  "HSCAN" => { first: 1, last: 1, step: 1, single_key: true },
  "HSET" => { first: 1, last: 1, step: 1, single_key: true },
  "HSETNX" => { first: 1, last: 1, step: 1, single_key: true },
  "HSTRLEN" => { first: 1, last: 1, step: 1, single_key: true },
  "HVALS" => { first: 1, last: 1, step: 1, single_key: true },
  "INCR" => { first: 1, last: 1, step: 1, single_key: true },
  "INCRBY" => { first: 1, last: 1, step: 1, single_key: true },
  "INCRBYFLOAT" => { first: 1, last: 1, step: 1, single_key: true },
  "LINDEX" => { first: 1, last: 1, step: 1, single_key: true },
  "LINSERT" => { first: 1, last: 1, step: 1, single_key: true },
  "LLEN" => { first: 1, last: 1, step: 1, single_key: true },
  "LMOVE" => { first: 1, last: 2, step: 1, single_key: false },
  "LPOP" => { first: 1, last: 1, step: 1, single_key: true },
  "LPOS" => { first: 1, last: 1, step: 1, single_key: true },
  "LPUSH" => { first: 1, last: 1, step: 1, single_key: true },
  "LPUSHX" => { first: 1, last: 1, step: 1, single_key: true },
  "LRANGE" => { first: 1, last: 1, step: 1, single_key: true },
  "LREM" => { first: 1, last: 1, step: 1, single_key: true },
  "LSET" => { first: 1, last: 1, step: 1, single_key: true },
  "LTRIM" => { first: 1, last: 1, step: 1, single_key: true },
  "MGET" => { first: 1, last: -1, step: 1, single_key: false },
  "MIGRATE" => { first: 3, last: 3, step: 1, single_key: true },
  "MOVE" => { first: 1, last: 1, step: 1, single_key: true },
  "MSET" => { first: 1, last: -1, step: 2, single_key: false },
  "MSETNX" => { first: 1, last: -1, step: 2, single_key: false },
  "OBJECT" => { first: 2, last: 2, step: 1, single_key: true },
  "PERSIST" => { first: 1, last: 1, step: 1, single_key: true },
  "PEXPIRE" => { first: 1, last: 1, step: 1, single_key: true },
  "PEXPIREAT" => { first: 1, last: 1, step: 1, single_key: true },
  "PFADD" => { first: 1, last: 1, step: 1, single_key: true },
  "PFCOUNT" => { first: 1, last: -1, step: 1, single_key: false },
  "PFDEBUG" => { first: 2, last: 2, step: 1, single_key: true },
  "PFMERGE" => { first: 1, last: -1, step: 1, single_key: false },
  "PSETEX" => { first: 1, last: 1, step: 1, single_key: true },
  "PTTL" => { first: 1, last: 1, step: 1, single_key: true },
  "RENAME" => { first: 1, last: 2, step: 1, single_key: false },
  "RENAMENX" => { first: 1, last: 2, step: 1, single_key: false },
  "RESTORE" => { first: 1, last: 1, step: 1, single_key: true },
  "RESTORE-ASKING" => { first: 1, last: 1, step: 1, single_key: true },
  "RPOP" => { first: 1, last: 1, step: 1, single_key: true },
  "RPOPLPUSH" => { first: 1, last: 2, step: 1, single_key: false },
  "RPUSH" => { first: 1, last: 1, step: 1, single_key: true },
  "RPUSHX" => { first: 1, last: 1, step: 1, single_key: true },
  "SADD" => { first: 1, last: 1, step: 1, single_key: true },
  "SCARD" => { first: 1, last: 1, step: 1, single_key: true },
  "SDIFF" => { first: 1, last: -1, step: 1, single_key: false },
  "SDIFFSTORE" => { first: 1, last: -1, step: 1, single_key: false },
  "SET" => { first: 1, last: 1, step: 1, single_key: true },
  "SETBIT" => { first: 1, last: 1, step: 1, single_key: true },
  "SETEX" => { first: 1, last: 1, step: 1, single_key: true },
  "SETNX" => { first: 1, last: 1, step: 1, single_key: true },
  "SETRANGE" => { first: 1, last: 1, step: 1, single_key: true },
  "SINTER" => { first: 1, last: -1, step: 1, single_key: false },
  "SINTERSTORE" => { first: 1, last: -1, step: 1, single_key: false },
  "SISMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
  "SMEMBERS" => { first: 1, last: 1, step: 1, single_key: true },
  "SMISMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
  "SMOVE" => { first: 1, last: 2, step: 1, single_key: false },
  "SORT" => { first: 1, last: 1, step: 1, single_key: true },
  "SPOP" => { first: 1, last: 1, step: 1, single_key: true },
  "SRANDMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
  "SREM" => { first: 1, last: 1, step: 1, single_key: true },
  "SSCAN" => { first: 1, last: 1, step: 1, single_key: true },
  "STRLEN" => { first: 1, last: 1, step: 1, single_key: true },
  "SUBSTR" => { first: 1, last: 1, step: 1, single_key: true },
  "SUNION" => { first: 1, last: -1, step: 1, single_key: false },
  "SUNIONSTORE" => { first: 1, last: -1, step: 1, single_key: false },
  "TOUCH" => { first: 1, last: -1, step: 1, single_key: false },
  "TTL" => { first: 1, last: 1, step: 1, single_key: true },
  "TYPE" => { first: 1, last: 1, step: 1, single_key: true },
  "UNLINK" => { first: 1, last: -1, step: 1, single_key: false },
  "WATCH" => { first: 1, last: -1, step: 1, single_key: false },
  "XACK" => { first: 1, last: 1, step: 1, single_key: true },
  "XADD" => { first: 1, last: 1, step: 1, single_key: true },
  "XAUTOCLAIM" => { first: 1, last: 1, step: 1, single_key: true },
  "XCLAIM" => { first: 1, last: 1, step: 1, single_key: true },
  "XDEL" => { first: 1, last: 1, step: 1, single_key: true },
  "XGROUP" => { first: 2, last: 2, step: 1, single_key: true },
  "XINFO" => { first: 2, last: 2, step: 1, single_key: true },
  "XLEN" => { first: 1, last: 1, step: 1, single_key: true },
  "XPENDING" => { first: 1, last: 1, step: 1, single_key: true },
  "XRANGE" => { first: 1, last: 1, step: 1, single_key: true },
  "XREVRANGE" => { first: 1, last: 1, step: 1, single_key: true },
  "XSETID" => { first: 1, last: 1, step: 1, single_key: true },
  "XTRIM" => { first: 1, last: 1, step: 1, single_key: true },
  "ZADD" => { first: 1, last: 1, step: 1, single_key: true },
  "ZCARD" => { first: 1, last: 1, step: 1, single_key: true },
  "ZCOUNT" => { first: 1, last: 1, step: 1, single_key: true },
  "ZDIFFSTORE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZINCRBY" => { first: 1, last: 1, step: 1, single_key: true },
  "ZINTERSTORE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZLEXCOUNT" => { first: 1, last: 1, step: 1, single_key: true },
  "ZMSCORE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZPOPMAX" => { first: 1, last: 1, step: 1, single_key: true },
  "ZPOPMIN" => { first: 1, last: 1, step: 1, single_key: true },
  "ZRANDMEMBER" => { first: 1, last: 1, step: 1, single_key: true },
  "ZRANGE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZRANGEBYLEX" => { first: 1, last: 1, step: 1, single_key: true },
  "ZRANGEBYSCORE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZRANGESTORE" => { first: 1, last: 2, step: 1, single_key: false },
  "ZRANK" => { first: 1, last: 1, step: 1, single_key: true },
  "ZREM" => { first: 1, last: 1, step: 1, single_key: true },
  "ZREMRANGEBYLEX" => { first: 1, last: 1, step: 1, single_key: true },
  "ZREMRANGEBYRANK" => { first: 1, last: 1, step: 1, single_key: true },
  "ZREMRANGEBYSCORE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZREVRANGE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZREVRANGEBYLEX" => { first: 1, last: 1, step: 1, single_key: true },
  "ZREVRANGEBYSCORE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZREVRANK" => { first: 1, last: 1, step: 1, single_key: true },
  "ZSCAN" => { first: 1, last: 1, step: 1, single_key: true },
  "ZSCORE" => { first: 1, last: 1, step: 1, single_key: true },
  "ZUNIONSTORE" => { first: 1, last: 1, step: 1, single_key: true }
}.freeze
CrossSlotError =
Class.new(StandardError)

Class Method Summary collapse

Class Method Details

.allow_cross_slot_commandsObject

Keep track of the call stack to allow nested calls to work.



204
205
206
207
208
209
210
211
# File 'lib/gitlab/instrumentation/redis_cluster_validator.rb', line 204

def allow_cross_slot_commands
  Thread.current[:allow_cross_slot_commands] ||= 0
  Thread.current[:allow_cross_slot_commands] += 1

  yield
ensure
  Thread.current[:allow_cross_slot_commands] -= 1
end

.allow_cross_slot_commands?Boolean

Returns:

  • (Boolean)


213
214
215
# File 'lib/gitlab/instrumentation/redis_cluster_validator.rb', line 213

def allow_cross_slot_commands?
  Thread.current[:allow_cross_slot_commands].to_i > 0
end

.validate(commands) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
# File 'lib/gitlab/instrumentation/redis_cluster_validator.rb', line 186

def validate(commands)
  return if commands.empty?

  # early exit for single-command (non-pipelined) if it is a single-key-command
  command_name = commands.size > 1 ? "PIPELINE/MULTI" : commands.first.first.to_s.upcase
  return if commands.size == 1 && REDIS_COMMANDS.dig(command_name, :single_key)

  keys = commands.map { |command| extract_keys(command) }.flatten

  {
    valid: !has_cross_slot_keys?(keys),
    command_name: command_name,
    key_count: keys.size,
    allowed: allow_cross_slot_commands?
  }
end