nchan_tools 0.1.7 → 0.1.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9cce7f7cc54b6764afddca855f9e1a58d929f05a81b7d364489ff87d9f298e81
4
- data.tar.gz: 9892571bd84b6779007eaf94a151f1d0e42209396848bddf22e3bcfabe83475d
3
+ metadata.gz: 2c506959c2eba296db96ed5e1598412798117f4e33bae4a909e223148a829b4a
4
+ data.tar.gz: 4f178a9eac6fa954d06b5f9f4d6d21cb5c3e6320390e3f422fdcb0589e5276d9
5
5
  SHA512:
6
- metadata.gz: 23fb2e18d4885b2cdff8fbb8e79ed1b99fd65ff51f247ba908b24bb604c0a541c7d553e264578a8d799c7edd35c545bb9275e50704ea20b2009c363f459ec425
7
- data.tar.gz: fbdfd34328967212fcbff8f491cf2109da6ba5b3856d07abe654992197fad874bb844a5a7ec7ac26b4db0b177d2e37bfb1b8d45c33c4ecc4a3cb60192818bf07
6
+ metadata.gz: 183363cebe229289e9c3eb0c0989a2816b7bb06c2f3650a81fdad8b5a22c1a2502bd182081a49bbcdf076d0eef219f0ae8f3ccff6a31252e8a137d1940347231
7
+ data.tar.gz: e5c09c1e6759dd835fb9699a96e63e6d2bbb7ae7adefac324cae19083f0921d3699fc88bdeb54dde4810153ed9cf528d22f9829688c8f9a02fb2f547f507debf
@@ -4,32 +4,31 @@ require "redis"
4
4
  require "optparse"
5
5
  require 'nchan_tools/rdsck'
6
6
 
7
- $opt = {
7
+ opt = {
8
8
  url: "redis://127.0.0.1:6379/",
9
9
  verbose: false,
10
10
  command: nil
11
11
  }
12
12
 
13
- def dbg(...)
14
- if $opt[:verbose]
15
- print("# ")
16
- puts(...)
17
- end
18
- end
19
-
20
13
  opt_parser=OptionParser.new do |opts|
21
- opts.on("--url", "--url REDIS_URL (#{$opt[:url]})", "Redis server and port..") do |v|
22
- $opt[:url]=v
14
+ opts.on("--url", "--url REDIS_URL (#{opt[:url]})", "Redis server and port..") do |v|
15
+ opt[:url]=v
23
16
  end
24
17
  opts.on("-q", "--quiet", "output only results without any other information") do
25
- $opt[:quiet]=false
18
+ opt[:quiet]=true
26
19
  end
27
20
  opts.on("--list-channels", "list all Nchan channels on Redis server or cluster") do |v|
28
- $opt[:command]=:filter_channels
21
+ opt[:command]=:filter_channels
29
22
  end
23
+ opts.on("--watch-channels", "watch for changes in Nchan channels on Redis server or cluster") do |v|
24
+ opt[:command]=:watch_channels
25
+ end
26
+ opts.on("--set-notify-keyspace-events", "when using --watch-channels, sets the notify-keyspace-events Redis config before starting. May not be possible if the redis 'config' command is disabled") do
27
+ opt[:set_notify_keyspace_events] = true
28
+ end
30
29
  opts.on("--filter-channels-min-subscribers=[NUMBER]") do |v|
31
- $opt[:command]=:filter_channels
32
- $opt[:min_subscribers]=v.to_i
30
+ opt[:command]||=:filter_channels
31
+ opt[:min_subscribers]=v.to_i
33
32
  end
34
33
  end
35
34
  opt_parser.banner= <<~EOB
@@ -46,18 +45,23 @@ opt_parser.banner= <<~EOB
46
45
  EOB
47
46
  opt_parser.parse!
48
47
 
49
- rdsck = Rdsck.new $opt
48
+ opt[:verbose] = !opt[:quiet]
49
+
50
+ rdsck = Rdsck.new opt
50
51
  if not rdsck.connect
51
- STDERR.puts "failed to connect to #{$opt[:url]}"
52
+ STDERR.puts "failed to connect to #{opt[:url]}"
52
53
  exit 1
53
54
  end
54
55
 
55
- case $opt[:command]
56
+ case opt[:command]
56
57
  when :filter_channels
57
- puts "# scanning for channels #{$opt[:min_subscribers] && "with subscribers >= #{$opt[:min_subscribers]}"}"
58
- chans = rdsck.filter_channels(min_subscribers: $opt[:min_subscribers])
59
- puts "# found #{chans.count} channel#{chans.count != 1 && "s"}#{chans.count == 0 ? "." : ":"}"
58
+ rdsck.dbg "scanning for channels #{opt[:min_subscribers] && "with subscribers >= #{opt[:min_subscribers]}"}"
59
+ chans = rdsck.filter_channels(min_subscribers: opt[:min_subscribers])
60
+ rdsck.dbg "found #{chans.count} channel#{chans.count != 1 && "s"}#{chans.count == 0 ? "." : ":"}"
60
61
  puts chans.join("\n")
62
+ when :watch_channels
63
+ rdsck.dbg "watching for channels #{opt[:min_subscribers] && "with subscribers >= #{opt[:min_subscribers]}"}"
64
+ rdsck.watch_channels({min_subscribers: opt[:min_subscribers]}, opt[:set_notify_keyspace_events])
61
65
  else
62
66
  puts "Nothing to do"
63
67
  end
@@ -1,10 +1,55 @@
1
1
  class Rdsck
2
+ require "async"
3
+ require "async/redis"
4
+
5
+ #doesn't support psubscribe by default. can you believe it?!
6
+ class Async::Redis::Context::Psubscribe < Async::Redis::Context::Subscribe
7
+ MESSAGE = 'pmessage'
8
+
9
+ def listen
10
+ while response = @connection.read_response
11
+ return response if response.first == MESSAGE
12
+ end
13
+ end
14
+
15
+ def subscribe(channels)
16
+ @connection.write_request ['PSUBSCRIBE', *channels]
17
+ @connection.flush
18
+ end
19
+
20
+ def unsubscribe(channels)
21
+ @connection.write_request ['PUNSUBSCRIBE', *channels]
22
+ @connection.flush
23
+ end
24
+ end
25
+
26
+ class Async::Redis::Client
27
+ def psubscribe(*channels)
28
+ context = Async::Redis::Context::Psubscribe.new(@pool, channels)
29
+ return context unless block_given?
30
+ begin
31
+ yield context
32
+ ensure
33
+ context.close
34
+ end
35
+ end
36
+ end
37
+
2
38
  attr_accessor :url, :verbose, :namespace
3
39
  attr_accessor :redis, :masters
40
+
41
+ def dbg(*args)
42
+ if @verbose
43
+ print("# ")
44
+ puts(*args)
45
+ end
46
+ end
47
+
4
48
  def initialize(opt)
5
49
  @url=opt[:url]
6
50
  @verbose=opt[:verbose]
7
51
  @namespace=opt[:namespace]
52
+ @channel_id=opt[:channel_id]
8
53
  end
9
54
 
10
55
  def cluster?
@@ -13,8 +58,10 @@ class Rdsck
13
58
 
14
59
  def connect
15
60
  begin
16
- @redis=Redis.new url: $opt[:url]
61
+ @redis=Redis.new url: @url
62
+
17
63
  mode = redis.info["redis_mode"]
64
+
18
65
  rescue StandardError => e
19
66
  STDERR.puts e.message
20
67
  return false
@@ -23,7 +70,7 @@ class Rdsck
23
70
  if mode == "cluster"
24
71
  @redis.close
25
72
  begin
26
- @redis=Redis.new cluster: [$opt[:url]]
73
+ @redis=Redis.new cluster: [@url]
27
74
  @redis.ping
28
75
  rescue StandardError => e
29
76
  STDERR.puts e.message
@@ -49,7 +96,7 @@ class Rdsck
49
96
  end
50
97
 
51
98
  def key(subkey=nil)
52
- k = "{channel:#{$opt[:namespace]}/#{$opt[:channel_id]}}"
99
+ k = "{channel:#{@namespace}/#{@channel_id}}"
53
100
  return subkey ? "#{k}:#{subkey}" : k
54
101
  end
55
102
 
@@ -60,6 +107,90 @@ class Rdsck
60
107
  #...
61
108
  end
62
109
 
110
+ class Watch
111
+ def initialize(rdsck, node, filters, set_notify_config = nil)
112
+ @rdsck = rdsck
113
+ @sync = node
114
+ @filters = filters
115
+ @set_notify_config = set_notify_config
116
+ @host, @port, @location = @sync.connection[:host], @sync.connection[:port]
117
+ @url = @sync.connection[:id]
118
+ @async = Async::Redis::Client.new(Async::IO::Endpoint.tcp(@host, @port))
119
+ if set_notify_config
120
+ @rdsck.dbg "set #{@url} notify-keyspace-events to \"Kh\""
121
+ @prev_notify_keyspace_event_config = @sync.config("get", "notify-keyspace-events")
122
+ @prev_notify_keyspace_event_config = @prev_notify_keyspace_event_config[1] if @prev_notify_keyspace_event_config
123
+
124
+ @sync.config :set, "notify-keyspace-events", "Kh"
125
+ end
126
+ end
127
+
128
+ def watch(task)
129
+ task.async do
130
+ #puts "subscribeme"
131
+ while true do
132
+ @async.psubscribe "__keyspace*__:{channel:*}" do |ctx|
133
+ type, pattern, name, msg = ctx.listen
134
+ #puts "TYPE: #{type}, PAT:#{pattern}, NAME:#{name}, MSG:#{msg}"
135
+ m=name.match(/^__.*__:(\{.*\})/)
136
+ if m && m[1]
137
+ key = m[1]
138
+
139
+ filtered = false
140
+ subs = nil
141
+
142
+ if @filters[:min_subscribers]
143
+ subs = @sync.hget key, "fake_subscribers"
144
+ subs = subs.to_i
145
+ filtered = true if subs < @filters[:min_subscribers]
146
+ end
147
+
148
+ if !filtered
149
+ if subs
150
+ puts "#{key} subscribers: #{subs}"
151
+ else
152
+ puts "#{key}"
153
+ end
154
+ end
155
+
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ def stop
163
+ @async.close
164
+ if @set_notify_config
165
+ @rdsck.dbg "set #{@url} notify-keyspace-events back to #{@prev_notify_keyspace_event_config}"
166
+ @sync.config(:set, "notify-keyspace-events", @prev_notify_keyspace_event_config)
167
+ end
168
+ end
169
+ end
170
+
171
+ def watch_channels(filters={}, set_notify_keyspace_events=nil)
172
+ watchers = []
173
+ @masters.each do |m|
174
+ watchers << Watch.new(self, m, filters, set_notify_keyspace_events)
175
+ end
176
+
177
+
178
+ begin
179
+ Async do |task|
180
+ watchers.each do |watcher|
181
+ watcher.watch(task)
182
+ end
183
+ end
184
+ rescue Interrupt => e
185
+ dbg "stopping watch"
186
+ ensure
187
+ watchers.each do |watcher|
188
+ watcher.stop
189
+ end
190
+ end
191
+
192
+ end
193
+
63
194
  def filter_channels(filters={})
64
195
  script = <<~EOF
65
196
  local prev_cursor = ARGV[1]
@@ -91,7 +222,7 @@ class Rdsck
91
222
 
92
223
  results = []
93
224
  batch_size=500
94
- masters.each do |m|
225
+ @masters.each do |m|
95
226
  hash = m.script "load", script
96
227
  cursor, pattern = "0", "{channel:*}"
97
228
  loop do
@@ -1,3 +1,3 @@
1
1
  module NchanTools
2
- VERSION = "0.1.7"
2
+ VERSION = "0.1.12"
3
3
  end
data/nchan_tools.gemspec CHANGED
@@ -29,6 +29,9 @@ Gem::Specification.new do |spec|
29
29
  spec.add_dependency "celluloid"
30
30
  spec.add_dependency "celluloid-io"
31
31
  spec.add_dependency "HDRHistogram"
32
+ spec.add_dependency "redis", "~>4.2.0"
33
+ spec.add_dependency "async"
34
+ spec.add_dependency "async-redis"
32
35
 
33
36
  spec.add_dependency "websocket-driver"
34
37
  spec.add_dependency 'websocket-extensions'
@@ -38,6 +41,6 @@ Gem::Specification.new do |spec|
38
41
  spec.add_dependency 'http-2'
39
42
  end
40
43
  spec.add_development_dependency "pry"
41
- spec.add_development_dependency "bundler", "~> 1.16"
42
- spec.add_development_dependency "rake", "~> 10.0"
44
+ spec.add_development_dependency "bundler"
45
+ spec.add_development_dependency "rake"
43
46
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nchan_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leo Ponomarev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-29 00:00:00.000000000 Z
11
+ date: 2021-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: typhoeus
@@ -94,6 +94,48 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: redis
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 4.2.0
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 4.2.0
111
+ - !ruby/object:Gem::Dependency
112
+ name: async
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: async-redis
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
97
139
  - !ruby/object:Gem::Dependency
98
140
  name: websocket-driver
99
141
  requirement: !ruby/object:Gem::Requirement
@@ -182,30 +224,30 @@ dependencies:
182
224
  name: bundler
183
225
  requirement: !ruby/object:Gem::Requirement
184
226
  requirements:
185
- - - "~>"
227
+ - - ">="
186
228
  - !ruby/object:Gem::Version
187
- version: '1.16'
229
+ version: '0'
188
230
  type: :development
189
231
  prerelease: false
190
232
  version_requirements: !ruby/object:Gem::Requirement
191
233
  requirements:
192
- - - "~>"
234
+ - - ">="
193
235
  - !ruby/object:Gem::Version
194
- version: '1.16'
236
+ version: '0'
195
237
  - !ruby/object:Gem::Dependency
196
238
  name: rake
197
239
  requirement: !ruby/object:Gem::Requirement
198
240
  requirements:
199
- - - "~>"
241
+ - - ">="
200
242
  - !ruby/object:Gem::Version
201
- version: '10.0'
243
+ version: '0'
202
244
  type: :development
203
245
  prerelease: false
204
246
  version_requirements: !ruby/object:Gem::Requirement
205
247
  requirements:
206
- - - "~>"
248
+ - - ">="
207
249
  - !ruby/object:Gem::Version
208
- version: '10.0'
250
+ version: '0'
209
251
  description: publishing, subscribing, testing, and benchmarking utilities for Nchan.
210
252
  email:
211
253
  - leo@nchan.io
@@ -254,7 +296,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
254
296
  - !ruby/object:Gem::Version
255
297
  version: '0'
256
298
  requirements: []
257
- rubygems_version: 3.1.4
299
+ rubygems_version: 3.2.7
258
300
  signing_key:
259
301
  specification_version: 4
260
302
  summary: Development and testing utilities for Nchan