class Rdsck

Attributes

masters[RW]
namespace[RW]
redis[RW]
url[RW]
verbose[RW]

Public Class Methods

new(opt) click to toggle source
# File lib/nchan_tools/rdsck.rb, line 48
def initialize(opt)
  @url=opt[:url]
  @verbose=opt[:verbose]
  @namespace=opt[:namespace]
  @channel_id=opt[:channel_id]
end

Public Instance Methods

cluster?() click to toggle source
# File lib/nchan_tools/rdsck.rb, line 55
def cluster?
  @cluster_mode
end
connect() click to toggle source
# File lib/nchan_tools/rdsck.rb, line 59
def connect
  begin
    @redis=Redis.new url: @url
    
    mode = redis.info["redis_mode"]
    
  rescue StandardError => e
    STDERR.puts e.message
    return false
  end

  if mode == "cluster"
    @redis.close
    begin
      @redis=Redis.new cluster: [@url]
      @redis.ping
    rescue StandardError => e
      STDERR.puts e.message
      return false
    end
    
    @cluster_mode = true
    @masters = []
    
    redis.connection.each do |c|
      node = Redis.new url: c[:id]
      @masters << node
    end
  else
    @masters = [@redis]
  end
  
  dbg "Connected to Redis #{mode == "cluster" ? "cluster" : "server"}"
  (Array === @redis.connection ? @redis.connection : [@redis.connection]) .each do |v|
    dbg "  #{v[:id]}"
  end
  self
end
dbg(*args) click to toggle source
# File lib/nchan_tools/rdsck.rb, line 41
def dbg(*args)
  if @verbose
    print("# ")
    puts(*args)
  end
end
filter_channels(filters={}) click to toggle source
# File lib/nchan_tools/rdsck.rb, line 194
  def filter_channels(filters={})
    script = <<~EOF
      local prev_cursor = ARGV[1]
      local pattern = ARGV[2]
      local scan_batch_size = ARGV[3]
      
      local min_subscribers = ARGV[4] and #ARGV[4] > 0 and tonumber(ARGV[4])
      
      local cursor, iteration 
      if pattern and #pattern > 0 then
        cursor, iteration = unpack(redis.call("SCAN", prev_cursor, "MATCH", pattern, "COUNT", scan_batch_size))
      else
        cursor, iteration = unpack(redis.call("SCAN", prev_cursor, "COUNT", scan_batch_size))
      end
      
      local matched = {}
      for _, chankey in pairs(iteration) do
        local match = true
        if min_subscribers then
          match = match and (tonumber(redis.call('HGET', chankey, 'fake_subscribers') or 0) >= min_subscribers)
        end
        if match then
          table.insert(matched, chankey)
        end
      end
      
      return {cursor, matched}
    EOF
    
    results = []
    batch_size=500
    @masters.each do |m|
      hash = m.script "load", script
      cursor, pattern = "0", "{channel:*}"
      loop do
        cursor, batch_results = m.evalsha hash, keys: [], argv: [cursor, pattern, batch_size, filters[:min_subscribers]]
        results += batch_results
        pattern = ""
        break if cursor.to_i == 0
      end
    end
    results
    
    results.map! do |key|
      m = key.match(/^\{channel\:(.*)\}$/)
      m[1] || key
    end
  end
info() click to toggle source
# File lib/nchan_tools/rdsck.rb, line 103
def info
  channel_hash=@redis.hgetall key
  hash_ttl=@redis.ttl key
  channel_subs=@redis.hgetall key("subscribers")
  #...
end
key(subkey=nil) click to toggle source
# File lib/nchan_tools/rdsck.rb, line 98
def key(subkey=nil)
  k = "{channel:#{@namespace}/#{@channel_id}}"
  return subkey ? "#{k}:#{subkey}" : k
end
watch_channels(filters={}, set_notify_keyspace_events=nil) click to toggle source
# File lib/nchan_tools/rdsck.rb, line 171
def watch_channels(filters={}, set_notify_keyspace_events=nil)
  watchers = []
  @masters.each do |m|
    watchers << Watch.new(self, m, filters, set_notify_keyspace_events)
  end


  begin
    Async do |task|
      watchers.each do |watcher|
        watcher.watch(task)
      end
    end
  rescue Interrupt => e
    dbg "stopping watch"
  ensure
    watchers.each do |watcher|
      watcher.stop
    end
  end
  
end