rdkit 0.0.1 → 0.1.4

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/Gemfile +10 -1
  4. data/Gemfile.ci +13 -0
  5. data/Guardfile +40 -0
  6. data/README.md +282 -6
  7. data/Vagrantfile +13 -0
  8. data/example/blocking.rb +10 -0
  9. data/example/blocking/command_runner.rb +28 -0
  10. data/example/blocking/core.rb +24 -0
  11. data/example/blocking/server.rb +10 -0
  12. data/example/blocking/version.rb +3 -0
  13. data/example/callbacks.rb +9 -0
  14. data/example/callbacks/command_runner.rb +21 -0
  15. data/example/callbacks/core.rb +18 -0
  16. data/example/callbacks/server.rb +30 -0
  17. data/example/counter.rb +0 -2
  18. data/example/counter/command_runner.rb +4 -0
  19. data/example/counter/core.rb +4 -0
  20. data/example/http.rb +9 -0
  21. data/example/http/core.rb +18 -0
  22. data/example/http/responder.rb +7 -0
  23. data/example/http/server.rb +19 -0
  24. data/lib/rdkit.rb +20 -3
  25. data/lib/rdkit/callbacks.rb +10 -0
  26. data/lib/rdkit/client.rb +157 -0
  27. data/lib/rdkit/configuration.rb +31 -0
  28. data/lib/rdkit/core.rb +2 -4
  29. data/lib/rdkit/core_ext.rb +7 -0
  30. data/lib/rdkit/db.rb +257 -0
  31. data/lib/rdkit/db_commands.rb +182 -0
  32. data/lib/rdkit/errors.rb +32 -1
  33. data/lib/rdkit/http_parser.rb +56 -0
  34. data/lib/rdkit/http_responder.rb +74 -0
  35. data/lib/rdkit/introspection.rb +133 -21
  36. data/lib/rdkit/logger.rb +9 -4
  37. data/lib/rdkit/memory_monitoring.rb +29 -0
  38. data/lib/rdkit/notification_center.rb +21 -0
  39. data/lib/rdkit/rd_object.rb +69 -0
  40. data/lib/rdkit/resp.rb +9 -1
  41. data/lib/rdkit/{command_parser.rb → resp_parser.rb} +6 -18
  42. data/lib/rdkit/resp_responder.rb +105 -0
  43. data/lib/rdkit/server.rb +242 -86
  44. data/lib/rdkit/simple_commands.rb +17 -0
  45. data/lib/rdkit/slow_log.rb +52 -0
  46. data/lib/rdkit/subcommands.rb +157 -0
  47. data/lib/rdkit/version.rb +1 -1
  48. data/rdkit.gemspec +6 -0
  49. metadata +119 -5
  50. data/lib/rdkit/inheritable.rb +0 -15
  51. data/lib/rdkit/resp_runner.rb +0 -46
data/lib/rdkit/errors.rb CHANGED
@@ -1,9 +1,40 @@
1
1
  module RDKit
2
2
  class RDKitError < StandardError; end
3
3
 
4
+ class WrongTypeError < RDKitError
5
+ def self.exception; new('Operation against a key holding the wrong kind of value'); end
6
+ end
7
+
4
8
  class ProtocolError < RDKitError; end
5
9
  class UnknownCommandError < ProtocolError; end
6
- class WrongNumberOfArgumentError < ProtocolError; end
10
+
11
+ class SyntaxError < ProtocolError
12
+ def self.exception; new('syntax error'); end
13
+ end
14
+
15
+ class UnknownSubcommandError < ProtocolError; end
16
+ class WrongNumberOfArgumentForSubcommandError < ProtocolError; end
17
+
18
+ class IllegalArgumentError < ProtocolError; end
19
+
20
+ class InvalidDBIndexError < IllegalArgumentError
21
+ def self.exception; new('invalid DB index'); end
22
+ end
23
+
24
+ class WrongNumberOfArgumentError < IllegalArgumentError; end
25
+
26
+ class IllegalClientNameError < IllegalArgumentError
27
+ def self.exception; new('Client names cannot contain spaces, newlines or special characters.'); end
28
+ end
29
+
30
+ class ValueNotAnIntegerOrOutOfRangeError < IllegalArgumentError
31
+ def self.exception; new('value is not an integer or out of range'); end
32
+ end
33
+
34
+ class ClientDisconnectedError < RDKitError; end
35
+ class NoSuchClientError < RDKitError
36
+ def self.exception; new('No such client'); end
37
+ end
7
38
 
8
39
  class NotImplementedError < RDKitError; end
9
40
 
@@ -0,0 +1,56 @@
1
+ require 'http/parser'
2
+
3
+ module RDKit
4
+ class HTTPParser
5
+ def initialize
6
+ @parser = HTTP::Parser.new
7
+
8
+ @responses = []
9
+
10
+ # https://github.com/tmm1/http_parser.rb
11
+
12
+ @body = nil
13
+
14
+ @parser.on_message_begin = proc do
15
+ end
16
+
17
+ @parser.on_headers_complete = proc do
18
+ # p @parser.http_method
19
+ # p @parser.request_url
20
+ # p @parser.headers
21
+
22
+ if (length = @parser.headers['Content-Length']) && length.to_i > 0
23
+ @body = ''
24
+ else
25
+ @responses << [@parser.http_method.downcase + "|" + @parser.request_url, nil]
26
+ end
27
+ end
28
+
29
+ @parser.on_body = proc do |chunk|
30
+ @body << chunk
31
+ end
32
+
33
+ @parser.on_message_complete = proc do
34
+ # Headers and body is all parsed
35
+ if @body
36
+ @responses << [@parser.http_method.downcase + "|" + @parser.request_url, @body]
37
+
38
+ @body = nil
39
+ end
40
+ end
41
+ end
42
+
43
+ def feed(data)
44
+ @parser << data
45
+ end
46
+
47
+ def gets
48
+ if result = @responses.shift
49
+
50
+ result
51
+ else
52
+ false
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,74 @@
1
+ require 'http/parser'
2
+ require 'cgi'
3
+ require 'rack'
4
+ require 'stringio'
5
+
6
+ module RDKit
7
+ class HTTPResponder
8
+ def run(cmd)
9
+ method, path = cmd.first.split('|')
10
+
11
+ status, headers, body = case method
12
+ when 'get'
13
+ if @@endpoints_get[path]
14
+ status, headers, body = @@endpoints_get[path].call
15
+
16
+ [status, headers.merge(default_headers), body]
17
+ else
18
+ error_404
19
+ end
20
+ else
21
+ error_404
22
+ end
23
+
24
+ response = Rack::Response.new(body, status, headers)
25
+
26
+ status, headers, body = response.finish
27
+
28
+ out = StringIO.new
29
+
30
+ out.print "HTTP/1.1: #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]}\r\n"
31
+
32
+ headers.each do |k, vs|
33
+ vs.split("\n").each { |v| out.print "#{k}: #{v}\r\n"; }
34
+ end
35
+ out.print "\r\n"
36
+ out.flush
37
+
38
+ body.each do |part|
39
+ out.print part; out.flush
40
+ end
41
+
42
+ out.string
43
+ end
44
+
45
+ def server
46
+ Server.instance
47
+ end
48
+
49
+ private
50
+
51
+ def error_400
52
+ [400, default_headers, []]
53
+ end
54
+
55
+ def error_404
56
+ [404, default_headers, ['Not Found']]
57
+ end
58
+
59
+ def default_headers
60
+ {
61
+ "Date" => CGI.rfc1123_date(Time.now),
62
+ "Server" => "RDKit",
63
+ "Connection" => "Keep-Alive",
64
+ "Content-Type" => "text/plain"
65
+ }
66
+ end
67
+
68
+ def self.get(path, &block)
69
+ @@endpoints_get ||= {}
70
+
71
+ @@endpoints_get[path] = block
72
+ end
73
+ end
74
+ end
@@ -1,45 +1,96 @@
1
1
  # 127.0.0.1:6379> info
2
2
  # # Server
3
- # redis_version:2.8.17
3
+ # redis_version:3.0.0
4
4
  # redis_git_sha1:00000000
5
5
  # redis_git_dirty:0
6
- # redis_build_id:32eb139b4f2b63
6
+ # redis_build_id:c8fc3cfae8617ea3
7
7
  # redis_mode:standalone
8
- # os:Darwin 13.4.0 x86_64
8
+ # os:Darwin 14.3.0 x86_64
9
9
  # arch_bits:64
10
10
  # multiplexing_api:kqueue
11
11
  # gcc_version:4.2.1
12
- # process_id:471
13
- # run_id:efa5b449cc3ba9c53f7bbb159a773e6ff20d575c
12
+ # process_id:6503
13
+ # run_id:42575aa7185796a6a5e5addc5991a54e4baaf95e
14
14
  # tcp_port:6379
15
- # uptime_in_seconds:1510124
16
- # uptime_in_days:17
15
+ # uptime_in_seconds:165102
16
+ # uptime_in_days:1
17
17
  # hz:10
18
+ # lru_clock:7932005
19
+ # config_file:/usr/local/etc/redis.conf
18
20
  #
19
21
  # # Clients
20
22
  # connected_clients:1
21
23
  # client_longest_output_list:0
22
24
  # client_biggest_input_buf:0
25
+ # blocked_clients:0
23
26
  #
24
27
  # # Memory
25
- # used_memory:124510288
26
- # used_memory_human:118.74M
27
- # used_memory_rss:119885824
28
- # used_memory_peak:124510288
29
- # used_memory_peak_human:118.74M
30
- # used_memory_lua:33792
31
- # mem_fragmentation_ratio:0.96
28
+ # used_memory:1009568
29
+ # used_memory_human:985.91K
30
+ # used_memory_rss:1728512
31
+ # used_memory_peak:3486144
32
+ # used_memory_peak_human:3.32M
33
+ # used_memory_lua:35840
34
+ # mem_fragmentation_ratio:1.71
32
35
  # mem_allocator:libc
33
36
  #
34
37
  # # Persistence
38
+ # loading:0
39
+ # rdb_changes_since_last_save:0
40
+ # rdb_bgsave_in_progress:0
41
+ # rdb_last_save_time:1433995353
42
+ # rdb_last_bgsave_status:ok
43
+ # rdb_last_bgsave_time_sec:0
44
+ # rdb_current_bgsave_time_sec:-1
45
+ # aof_enabled:0
46
+ # aof_rewrite_in_progress:0
47
+ # aof_rewrite_scheduled:0
48
+ # aof_last_rewrite_time_sec:-1
49
+ # aof_current_rewrite_time_sec:-1
50
+ # aof_last_bgrewrite_status:ok
51
+ # aof_last_write_status:ok
35
52
  #
36
53
  # # Stats
37
- # total_connections_received:74408
38
- # total_commands_processed:5122851
54
+ # total_connections_received:98
55
+ # total_commands_processed:265
39
56
  # instantaneous_ops_per_sec:0
57
+ # total_net_input_bytes:273320
58
+ # total_net_output_bytes:1024738
59
+ # instantaneous_input_kbps:0.00
60
+ # instantaneous_output_kbps:0.00
40
61
  # rejected_connections:0
62
+ # sync_full:0
63
+ # sync_partial_ok:0
64
+ # sync_partial_err:0
65
+ # expired_keys:0
66
+ # evicted_keys:0
67
+ # keyspace_hits:28
68
+ # keyspace_misses:11
41
69
  # pubsub_channels:0
42
70
  # pubsub_patterns:0
71
+ # latest_fork_usec:483
72
+ # migrate_cached_sockets:0
73
+ #
74
+ # # Replication
75
+ # role:master
76
+ # connected_slaves:0
77
+ # master_repl_offset:0
78
+ # repl_backlog_active:0
79
+ # repl_backlog_size:1048576
80
+ # repl_backlog_first_byte_offset:0
81
+ # repl_backlog_histlen:0
82
+ #
83
+ # # CPU
84
+ # used_cpu_sys:34.00
85
+ # used_cpu_user:13.61
86
+ # used_cpu_sys_children:0.05
87
+ # used_cpu_user_children:0.11
88
+ #
89
+ # # Cluster
90
+ # cluster_enabled:0
91
+ #
92
+ # # Keyspace
93
+ # db0:keys=3,expires=0,avg_ttl=0
43
94
 
44
95
  module RDKit
45
96
  module Introspection
@@ -48,15 +99,18 @@ module RDKit
48
99
 
49
100
  def initialize
50
101
  @data = {}
51
-
52
102
  @data.default = 0
53
103
  end
54
104
 
55
105
  module ClassMethods
56
106
  @@instance = Stats.new
57
107
 
58
- def incr(key)
59
- @@instance.data[key] += 1
108
+ def incr(key, amount=1)
109
+ @@instance.data[key] += amount
110
+ end
111
+
112
+ def clear(key)
113
+ @@instance.data[key] = 0
60
114
  end
61
115
 
62
116
  def info
@@ -67,13 +121,71 @@ module RDKit
67
121
  class << self; include ClassMethods; end
68
122
  end
69
123
 
124
+ class Commandstats
125
+ attr_reader :data
126
+
127
+ def initialize
128
+ @data = {}
129
+ @data.default = 0
130
+ end
131
+
132
+ module ClassMethods
133
+ @@instance = Commandstats.new
134
+
135
+ def reset
136
+ @@instance = Commandstats.new
137
+ end
138
+
139
+ def record(cmd, usec)
140
+ @@instance.data["#{cmd.downcase}_calls"] += 1
141
+ @@instance.data["#{cmd.downcase}_usec"] += usec
142
+ end
143
+
144
+ def info
145
+ cmds = @@instance.data.keys.map { |key| key.match(/^(.+)_/)[1] }.uniq
146
+
147
+ Hash[cmds.map do |cmd|
148
+ calls = @@instance.data["#{cmd}_calls"]
149
+ usec = @@instance.data["#{cmd}_usec"]
150
+
151
+ ["comstat_#{cmd}", "calls=#{calls},usec=#{usec},usec_per_call=#{'%.2f' % (usec.to_f / calls)}"]
152
+ end].sort_by { |k,v| k }
153
+ end
154
+ end
155
+
156
+ class << self; include ClassMethods; end
157
+ end
158
+
70
159
  module ClassMethods
71
160
  def register(server)
72
161
  @@server = server
73
162
  end
74
163
 
75
- def info
76
- @@server.introspection.merge({ stats: Stats.info })
164
+ def info(section)
165
+ default = @@server.introspection.merge({ stats: Stats.info })
166
+
167
+ case section.downcase
168
+ when 'default'
169
+ default
170
+ when 'all'
171
+ default.merge({ commandstats: Commandstats.info, gc: GC.stat, objspace: ObjectSpace.count_objects, allobjects: all_objects })
172
+ when 'commandstats'
173
+ { commandstats: Commandstats.info }
174
+ when 'gc'
175
+ { gc: GC.stat }
176
+ when 'objspace'
177
+ { objspace: ObjectSpace.count_objects, allobjects: all_objects }
178
+ else
179
+ default.keep_if { |k, v| k == section.downcase.to_sym }
180
+ end
181
+ end
182
+
183
+ private
184
+
185
+ def all_objects
186
+ all = {}
187
+ ObjectSpace.each_object { |o| all[o.class] = (all[o.class] || 0) + 1 }
188
+ all.sort_by { |k,v| -v }
77
189
  end
78
190
  end
79
191
 
data/lib/rdkit/logger.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  module RDKit
2
2
  class Logger
3
+ def initialize(log_path=nil)
4
+ @io = log_path ? File.open(log_path, 'a') : $stdout
5
+ end
6
+
3
7
  def debug(message)
4
8
  return unless $DEBUG && ENV['RACK_ENV'] != 'test'
5
9
 
@@ -16,12 +20,13 @@ module RDKit
16
20
 
17
21
  def log(message)
18
22
  case message
19
- when StandardError
20
- puts message.inspect
21
- puts message.backtrace.join("\n")
23
+ when Exception
24
+ @io.puts message.inspect
25
+ @io.puts message.backtrace.join("\n")
22
26
  else
23
- puts message
27
+ @io.puts message
24
28
  end
29
+ @io.flush
25
30
  end
26
31
  end
27
32
  end
@@ -0,0 +1,29 @@
1
+ require 'newrelic_rpm'
2
+
3
+ module RDKit
4
+ module MemoryMonitoring
5
+ @@peak_memory = 0
6
+
7
+ def used_memory_rss_in_mb
8
+ update_peak_memory!
9
+
10
+ '%0.2f' % used_memory_rss + 'M'
11
+ end
12
+
13
+ def used_memory_peak_in_mb
14
+ '%0.2f' % @@peak_memory + 'M'
15
+ end
16
+
17
+ private
18
+
19
+ def update_peak_memory!
20
+ @@peak_memory = [@@peak_memory, used_memory_rss].max
21
+ end
22
+
23
+ def used_memory_rss
24
+ @@sampler ||= NewRelic::Agent::Samplers::MemorySampler.new.sampler
25
+
26
+ @@sampler.get_sample
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ module RDKit
2
+ class NotificationCenter
3
+ module ClassMethods
4
+ @@channels = {}
5
+ @@channels.default = Hash.new
6
+
7
+ def publish(channel, message)
8
+ @@channels[channel.to_sym].each { |_, block| block.call(message) }
9
+ end
10
+
11
+ def subscribe(channel, client, &block)
12
+ @@channels[channel.to_sym][client] = block
13
+ end
14
+
15
+ def unsubscribe(channel, client)
16
+ @@channels[channel.to_sym].delete(client)
17
+ end
18
+ end
19
+ class << self; include ClassMethods; end
20
+ end
21
+ end