Class: SidekiqVigil::Storage

Inherits:
Object
  • Object
show all
Defined in:
lib/sidekiq_vigil/storage.rb

Defined Under Namespace

Classes: KeyDefinition, MissingTTL, UnmanagedPersistentKey

Constant Summary collapse

KEY_CATALOG =
[
  KeyDefinition.new(pattern: "stats:{yyyymmddHHMM}", type: "hash", ttl: "8 days", owner: "Reporter"),
  KeyDefinition.new(pattern: "exec:{queue}", type: "hash", ttl: "1 hour", owner: "Reporter"),
  KeyDefinition.new(pattern: "alerts", type: "hash", ttl: "managed", owner: "Alert::Manager"),
  KeyDefinition.new(pattern: "history:{alert_id}", type: "list", ttl: "24 hours", owner: "Alert::Manager"),
  KeyDefinition.new(pattern: "snapshot", type: "string", ttl: "interval × 4", owner: "Checker"),
  KeyDefinition.new(pattern: "leader", type: "string", ttl: "interval × 3", owner: "LeaderElection"),
  KeyDefinition.new(pattern: "mem:{process_id}", type: "string", ttl: "flush interval × 3", owner: "Reporter"),
  KeyDefinition.new(pattern: "config_digest", type: "hash", ttl: "1 hour", owner: "Reporter"),
  KeyDefinition.new(pattern: "mute", type: "string", ttl: "requested duration", owner: "Alert::Mute"),
  KeyDefinition.new(
    pattern: "check_state:{check-specific-suffix}",
    type: "string",
    ttl: "check-specific",
    owner: "Checks"
  )
].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(redis: nil, key_prefix: "default") ⇒ Storage

Returns a new instance of Storage.



31
32
33
34
# File 'lib/sidekiq_vigil/storage.rb', line 31

def initialize(redis: nil, key_prefix: "default")
  @redis = redis
  @prefix = "vigil:#{key_prefix}:"
end

Instance Attribute Details

#prefixObject (readonly)

Returns the value of attribute prefix.



29
30
31
# File 'lib/sidekiq_vigil/storage.rb', line 29

def prefix
  @prefix
end

Instance Method Details

#acquire_lock(suffix, token, ttl_ms:) ⇒ Object

Raises:



124
125
126
127
128
# File 'lib/sidekiq_vigil/storage.rb', line 124

def acquire_lock(suffix, token, ttl_ms:)
  raise MissingTTL, "a positive ttl is required" unless ttl_ms.is_a?(Numeric) && ttl_ms.positive?

  with_redis { |redis| redis.call("SET", key(suffix), token, "NX", "PX", ttl_ms) == "OK" }
end

#delete(suffix) ⇒ Object



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

def delete(suffix)
  with_redis { |redis| redis.call("DEL", key(suffix)) }
end

#eval(script, keys:, argv:) ⇒ Object



130
131
132
133
# File 'lib/sidekiq_vigil/storage.rb', line 130

def eval(script, keys:, argv:)
  namespaced_keys = keys.map { |item| key(item) }
  with_redis { |redis| redis.call("EVAL", script, namespaced_keys.length, *namespaced_keys, *argv) }
end

#get(suffix) ⇒ Object



45
46
47
# File 'lib/sidekiq_vigil/storage.rb', line 45

def get(suffix)
  with_redis { |redis| redis.call("GET", key(suffix)) }
end

#hash_delete(suffix, *fields) ⇒ Object



86
87
88
89
90
# File 'lib/sidekiq_vigil/storage.rb', line 86

def hash_delete(suffix, *fields)
  return if fields.empty?

  with_redis { |redis| redis.call("HDEL", key(suffix), *fields) }
end

#hash_get_all(suffix) ⇒ Object



82
83
84
# File 'lib/sidekiq_vigil/storage.rb', line 82

def hash_get_all(suffix)
  with_redis { |redis| redis.call("HGETALL", key(suffix)) }
end

#hash_increment(suffix, values, ttl:) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/sidekiq_vigil/storage.rb', line 61

def hash_increment(suffix, values, ttl:)
  require_ttl!(ttl)
  return if values.empty?

  with_redis do |redis|
    redis.pipelined do |pipeline|
      values.each do |field, amount|
        command = amount.is_a?(Integer) ? "HINCRBY" : "HINCRBYFLOAT"
        pipeline.call(command, key(suffix), field, amount)
      end
      pipeline.call("PEXPIRE", key(suffix), ttl_ms(ttl))
    end
  end
end

#hash_write(suffix, values, ttl:) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
# File 'lib/sidekiq_vigil/storage.rb', line 49

def hash_write(suffix, values, ttl:)
  require_ttl!(ttl)
  return if values.empty?

  with_redis do |redis|
    redis.pipelined do |pipeline|
      pipeline.call("HSET", key(suffix), *values.flatten)
      pipeline.call("PEXPIRE", key(suffix), ttl_ms(ttl))
    end
  end
end

#info(section = nil) ⇒ Object



139
140
141
# File 'lib/sidekiq_vigil/storage.rb', line 139

def info(section = nil)
  with_redis { |redis| parse_info(redis.call("INFO", *Array(section))) }
end

#key(suffix) ⇒ Object



36
37
38
# File 'lib/sidekiq_vigil/storage.rb', line 36

def key(suffix)
  "#{prefix}#{suffix}"
end

#list_push(suffix, value, ttl:, limit: 30) ⇒ Object



92
93
94
95
96
97
98
99
100
101
# File 'lib/sidekiq_vigil/storage.rb', line 92

def list_push(suffix, value, ttl:, limit: 30)
  require_ttl!(ttl)
  with_redis do |redis|
    redis.pipelined do |pipeline|
      pipeline.call("RPUSH", key(suffix), value)
      pipeline.call("LTRIM", key(suffix), -limit, -1)
      pipeline.call("PEXPIRE", key(suffix), ttl_ms(ttl))
    end
  end
end

#list_range(suffix) ⇒ Object



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

def list_range(suffix)
  with_redis { |redis| redis.call("LRANGE", key(suffix), 0, -1) }
end

#managed_hash_write(suffix, field, value) ⇒ Object



76
77
78
79
80
# File 'lib/sidekiq_vigil/storage.rb', line 76

def managed_hash_write(suffix, field, value)
  raise UnmanagedPersistentKey, "only alerts may be written without a ttl" unless suffix == "alerts"

  with_redis { |redis| redis.call("HSET", key(suffix), field, value) }
end

#pingObject



135
136
137
# File 'lib/sidekiq_vigil/storage.rb', line 135

def ping
  with_redis { |redis| redis.call("PING") }
end

#scan(pattern = "*") ⇒ Object



111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/sidekiq_vigil/storage.rb', line 111

def scan(pattern = "*")
  with_redis do |redis|
    cursor = "0"
    keys = []
    loop do
      cursor, batch = redis.call("SCAN", cursor, "MATCH", key(pattern), "COUNT", 100)
      keys.concat(batch)
      break if cursor == "0"
    end
    keys
  end
end

#set(suffix, value, ttl:) ⇒ Object



40
41
42
43
# File 'lib/sidekiq_vigil/storage.rb', line 40

def set(suffix, value, ttl:)
  require_ttl!(ttl)
  with_redis { |redis| redis.call("SET", key(suffix), value, "PX", ttl_ms(ttl)) }
end