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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/Gemfile +10 -1
- data/Gemfile.ci +13 -0
- data/Guardfile +40 -0
- data/README.md +282 -6
- data/Vagrantfile +13 -0
- data/example/blocking.rb +10 -0
- data/example/blocking/command_runner.rb +28 -0
- data/example/blocking/core.rb +24 -0
- data/example/blocking/server.rb +10 -0
- data/example/blocking/version.rb +3 -0
- data/example/callbacks.rb +9 -0
- data/example/callbacks/command_runner.rb +21 -0
- data/example/callbacks/core.rb +18 -0
- data/example/callbacks/server.rb +30 -0
- data/example/counter.rb +0 -2
- data/example/counter/command_runner.rb +4 -0
- data/example/counter/core.rb +4 -0
- data/example/http.rb +9 -0
- data/example/http/core.rb +18 -0
- data/example/http/responder.rb +7 -0
- data/example/http/server.rb +19 -0
- data/lib/rdkit.rb +20 -3
- data/lib/rdkit/callbacks.rb +10 -0
- data/lib/rdkit/client.rb +157 -0
- data/lib/rdkit/configuration.rb +31 -0
- data/lib/rdkit/core.rb +2 -4
- data/lib/rdkit/core_ext.rb +7 -0
- data/lib/rdkit/db.rb +257 -0
- data/lib/rdkit/db_commands.rb +182 -0
- data/lib/rdkit/errors.rb +32 -1
- data/lib/rdkit/http_parser.rb +56 -0
- data/lib/rdkit/http_responder.rb +74 -0
- data/lib/rdkit/introspection.rb +133 -21
- data/lib/rdkit/logger.rb +9 -4
- data/lib/rdkit/memory_monitoring.rb +29 -0
- data/lib/rdkit/notification_center.rb +21 -0
- data/lib/rdkit/rd_object.rb +69 -0
- data/lib/rdkit/resp.rb +9 -1
- data/lib/rdkit/{command_parser.rb → resp_parser.rb} +6 -18
- data/lib/rdkit/resp_responder.rb +105 -0
- data/lib/rdkit/server.rb +242 -86
- data/lib/rdkit/simple_commands.rb +17 -0
- data/lib/rdkit/slow_log.rb +52 -0
- data/lib/rdkit/subcommands.rb +157 -0
- data/lib/rdkit/version.rb +1 -1
- data/rdkit.gemspec +6 -0
- metadata +119 -5
- data/lib/rdkit/inheritable.rb +0 -15
- data/lib/rdkit/resp_runner.rb +0 -46
@@ -0,0 +1,69 @@
|
|
1
|
+
module RDKit
|
2
|
+
class RDObject
|
3
|
+
attr_accessor :type, :encoding, :value
|
4
|
+
|
5
|
+
def self.forward_to_value(*methods)
|
6
|
+
@forwarded_methods = methods
|
7
|
+
end
|
8
|
+
|
9
|
+
def method_missing(method, *args)
|
10
|
+
if forwarded_methods.include?(method)
|
11
|
+
value.__send__(method, *args)
|
12
|
+
else
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def forwarded_methods
|
20
|
+
self.class.instance_variable_get(:@forwarded_methods) || []
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
def string(value)
|
25
|
+
new.tap do |object|
|
26
|
+
object.type = :string
|
27
|
+
object.value = value
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def list(elements)
|
32
|
+
RDList.new.tap do |object|
|
33
|
+
object.type = :list
|
34
|
+
object.value = elements
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def set(elements)
|
39
|
+
require "set"
|
40
|
+
|
41
|
+
RDSet.new.tap do |set|
|
42
|
+
set.type = :set
|
43
|
+
set.value = Set.new(elements)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_hash(key, value)
|
48
|
+
RDHash.new.tap do |hash|
|
49
|
+
hash.type = :hash
|
50
|
+
hash.value = { key => value }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class << self; include ClassMethods; end
|
56
|
+
end
|
57
|
+
|
58
|
+
class RDList < RDObject
|
59
|
+
forward_to_value :unshift, :length, :shift, :empty?, :pop
|
60
|
+
end
|
61
|
+
|
62
|
+
class RDSet < RDObject
|
63
|
+
forward_to_value :add, :size, :to_a, :include?, :delete
|
64
|
+
end
|
65
|
+
|
66
|
+
class RDHash < RDObject
|
67
|
+
forward_to_value :has_key?, :[]=, :[], :size, :delete, :keys, :values
|
68
|
+
end
|
69
|
+
end
|
data/lib/rdkit/resp.rb
CHANGED
@@ -5,6 +5,12 @@ module RDKit
|
|
5
5
|
module ClassMethods
|
6
6
|
def compose(data)
|
7
7
|
case data
|
8
|
+
when *%w{ OK string list set hash zset none }
|
9
|
+
"+#{data}\r\n"
|
10
|
+
when true
|
11
|
+
":1\r\n"
|
12
|
+
when false
|
13
|
+
":0\r\n"
|
8
14
|
when Integer
|
9
15
|
":#{data}\r\n"
|
10
16
|
when Array
|
@@ -12,8 +18,10 @@ module RDKit
|
|
12
18
|
when NilClass
|
13
19
|
# Null Bulk String, not Null Array of "*-1\r\n"
|
14
20
|
"$-1\r\n"
|
21
|
+
when WrongTypeError
|
22
|
+
"-WRONGTYPE #{data.message}\r\n"
|
15
23
|
when StandardError
|
16
|
-
"
|
24
|
+
"-ERR #{data.message}\r\n"
|
17
25
|
else
|
18
26
|
# always Bulk String
|
19
27
|
"$#{data.bytesize}\r\n#{data}\r\n"
|
@@ -3,43 +3,31 @@ require "hiredis/reader"
|
|
3
3
|
# Hiredis::Reader does not handle inline commands, so
|
4
4
|
|
5
5
|
module RDKit
|
6
|
-
class
|
6
|
+
class RESPParser
|
7
7
|
def initialize
|
8
8
|
@reader = Hiredis::Reader.new
|
9
9
|
@buffer = []
|
10
10
|
@regexp = Regexp.new("\\A(.+)\\r\\n\\z")
|
11
|
-
@
|
11
|
+
@inline_mode = true
|
12
12
|
end
|
13
13
|
|
14
14
|
def feed(data)
|
15
|
-
if data =~ @regexp
|
15
|
+
if @inline_mode && (data =~ @regexp)
|
16
16
|
@buffer << $1.split
|
17
17
|
else
|
18
|
-
@
|
18
|
+
@inline_mode = false
|
19
19
|
|
20
|
-
|
20
|
+
@reader.feed(data)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
def gets
|
25
|
-
|
26
|
-
|
27
|
-
if result = @buffer.shift
|
25
|
+
if @inline_mode && (result = @buffer.shift)
|
28
26
|
|
29
27
|
result
|
30
28
|
else
|
31
29
|
@reader.gets
|
32
30
|
end
|
33
31
|
end
|
34
|
-
|
35
|
-
private
|
36
|
-
|
37
|
-
def read_into_buffer!
|
38
|
-
until (reply = @reader.gets) == false
|
39
|
-
@buffer << reply
|
40
|
-
end
|
41
|
-
rescue RuntimeError => e
|
42
|
-
@error = ProtocolError.new(e) if e.message =~ /Protocol error/
|
43
|
-
end
|
44
32
|
end
|
45
33
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module RDKit
|
2
|
+
class RESPResponder
|
3
|
+
def run(cmd)
|
4
|
+
if cmd.respond_to?(:call)
|
5
|
+
RESP.compose(cmd.call)
|
6
|
+
else
|
7
|
+
RESP.compose(call(cmd))
|
8
|
+
end
|
9
|
+
rescue StandardError => e
|
10
|
+
RESP.compose(e)
|
11
|
+
end
|
12
|
+
|
13
|
+
include SimpleCommands
|
14
|
+
include DBCommands
|
15
|
+
|
16
|
+
# 获取服务器状态
|
17
|
+
def info(section='default')
|
18
|
+
info = Introspection.info(section)
|
19
|
+
|
20
|
+
unless info.empty?
|
21
|
+
info.map do |type, value|
|
22
|
+
"# #{type.capitalize}\r\n" + value.map { |k, v| "#{k}:#{v}" }.join("\r\n") + "\r\n"
|
23
|
+
end.join("\r\n") + "\r\n"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def gc
|
28
|
+
GC.start
|
29
|
+
|
30
|
+
'OK'
|
31
|
+
end
|
32
|
+
|
33
|
+
def heapdump
|
34
|
+
require "objspace"
|
35
|
+
|
36
|
+
ObjectSpace.trace_object_allocations_start
|
37
|
+
|
38
|
+
GC.start
|
39
|
+
|
40
|
+
file = "tmp/heap-#{Process.pid}-#{Time.now.to_i}.json"
|
41
|
+
|
42
|
+
ObjectSpace.dump_all(output: File.open(file, "w"))
|
43
|
+
|
44
|
+
file
|
45
|
+
end
|
46
|
+
|
47
|
+
def monitor
|
48
|
+
server.monitors << server.current_client
|
49
|
+
|
50
|
+
'OK'
|
51
|
+
end
|
52
|
+
|
53
|
+
def shutdown
|
54
|
+
server.stop
|
55
|
+
end
|
56
|
+
|
57
|
+
include Subcommands
|
58
|
+
def config(cmd, *args)
|
59
|
+
execute_subcommand('config', %w{ get set resetstat }, cmd, *args)
|
60
|
+
end
|
61
|
+
|
62
|
+
def slowlog(cmd, *args)
|
63
|
+
execute_subcommand('slowlog', %w{ get reset len }, cmd, *args)
|
64
|
+
end
|
65
|
+
|
66
|
+
def client(cmd, *args)
|
67
|
+
execute_subcommand('client', %w{ list kill getname setname }, cmd, *args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def debug(cmd, *args)
|
71
|
+
execute_subcommand('debug', %w{ sleep segfault }, cmd, *args)
|
72
|
+
end
|
73
|
+
|
74
|
+
def server
|
75
|
+
Server.instance
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def call(cmd)
|
81
|
+
@logger ||= Logger.new
|
82
|
+
|
83
|
+
Introspection::Stats.incr(:total_commands_processed)
|
84
|
+
|
85
|
+
@logger.debug "running command: #{cmd}"
|
86
|
+
cmd, *args = cmd
|
87
|
+
|
88
|
+
cmd.downcase!
|
89
|
+
|
90
|
+
if self.respond_to?(cmd)
|
91
|
+
self.__send__(cmd, *args)
|
92
|
+
else
|
93
|
+
raise UnknownCommandError, "unknown command '#{cmd}'"
|
94
|
+
end
|
95
|
+
rescue ArgumentError => e
|
96
|
+
raise WrongNumberOfArgumentError, "wrong number of arguments for '#{cmd}' command"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
class RESPRunner < RESPResponder
|
101
|
+
def self.inherited(base)
|
102
|
+
$stderr.puts "RESPRunner is deprecated, use RESPResponder instead"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/rdkit/server.rb
CHANGED
@@ -1,61 +1,153 @@
|
|
1
|
-
require
|
1
|
+
require "sigdump/setup"
|
2
|
+
require 'thread/pool'
|
2
3
|
|
3
4
|
module RDKit
|
4
5
|
class Server
|
5
6
|
HZ = 10
|
6
|
-
|
7
|
+
|
8
|
+
HANDLED_SIGNALS = [ :TERM, :INT, :HUP ]
|
7
9
|
|
8
10
|
attr_reader :server_up_since
|
9
|
-
attr_reader :
|
11
|
+
attr_reader :current_client
|
12
|
+
attr_reader :current_db
|
10
13
|
attr_reader :core
|
11
14
|
attr_reader :host, :port
|
15
|
+
attr_reader :logger
|
16
|
+
attr_reader :monitors
|
17
|
+
attr_reader :cycles
|
18
|
+
attr_accessor :parser_class
|
19
|
+
|
20
|
+
def responder
|
21
|
+
@responder ||= (( @runner && $stderr.puts("@runner is deprecated, use @responder instead") ) || @runner)
|
22
|
+
end
|
12
23
|
|
13
24
|
def initialize(host, port)
|
14
25
|
@host, @port = host, port
|
15
26
|
|
16
27
|
@cycles = 0
|
17
|
-
@peak_memory = 0
|
18
28
|
@peak_connected_clients = 0
|
29
|
+
@client_id_seq = 0
|
19
30
|
|
20
|
-
@clients
|
31
|
+
@clients = Hash.new
|
32
|
+
@blocked_clients = Hash.new
|
33
|
+
@monitors = []
|
21
34
|
|
22
|
-
@logger = Logger.new
|
35
|
+
@logger = Logger.new(ENV['RDKIT_LOG_PATH'])
|
36
|
+
@current_db = DB.new(0)
|
37
|
+
@all_dbs = [@current_db]
|
23
38
|
|
24
39
|
Introspection.register(self)
|
25
40
|
|
26
41
|
@server_up_since = Time.now
|
27
|
-
end
|
28
42
|
|
29
|
-
|
30
|
-
unless @host && @port
|
31
|
-
raise SDKRequirementNotMetError, '@host and @port are required for server to run'
|
32
|
-
end
|
43
|
+
@parser_class = RESPParser
|
33
44
|
|
34
|
-
|
35
|
-
raise SDKRequirementNotMetError, '@core is required to represent your business logics'
|
36
|
-
end
|
45
|
+
register_notification_observers!
|
37
46
|
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
Server.register(self)
|
48
|
+
|
49
|
+
# Self-pipe for deferred signal-handling http://www.sitepoint.com/the-self-pipe-trick-explained/
|
50
|
+
# Borrowed from `Foreman::Engine`
|
51
|
+
reader, writer = create_pipe
|
52
|
+
@selfpipe = { :reader => reader, :writer => writer }
|
53
|
+
@signal_queue = []
|
41
54
|
end
|
42
55
|
|
43
56
|
def start
|
44
57
|
sanity_check!
|
45
58
|
|
59
|
+
register_signal_handlers
|
60
|
+
|
46
61
|
@server_socket = TCPServer.new(@host, @port)
|
47
62
|
|
48
63
|
run_acceptor
|
49
64
|
end
|
50
65
|
|
51
66
|
def stop
|
52
|
-
@logger.warn "
|
67
|
+
@logger.warn "shutting down..."
|
53
68
|
exit
|
54
69
|
end
|
55
70
|
|
71
|
+
def create_pipe
|
72
|
+
IO.method(:pipe).arity.zero? ? IO.pipe : IO.pipe("BINARY")
|
73
|
+
end
|
74
|
+
|
75
|
+
def register_signal_handlers
|
76
|
+
HANDLED_SIGNALS.each do |sig|
|
77
|
+
if ::Signal.list.include? sig.to_s
|
78
|
+
trap(sig) { @signal_queue << sig ; notice_signal }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def notice_signal
|
84
|
+
@selfpipe[:writer].write_nonblock('.')
|
85
|
+
rescue Errno::EAGAIN
|
86
|
+
# Ignore writes that would block
|
87
|
+
rescue Errno::EINT
|
88
|
+
# Retry if another signal arrived while writing
|
89
|
+
retry
|
90
|
+
end
|
91
|
+
|
92
|
+
def handle_signals
|
93
|
+
while sig = @signal_queue.shift
|
94
|
+
handle_signal(sig)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Invoke the real handler for signal +sig+. This shouldn't be called directly
|
99
|
+
# by signal handlers, as it might invoke code which isn't re-entrant.
|
100
|
+
#
|
101
|
+
# @param [Symbol] sig the name of the signal to be handled
|
102
|
+
#
|
103
|
+
def handle_signal(sig)
|
104
|
+
case sig
|
105
|
+
when :TERM
|
106
|
+
handle_term_signal
|
107
|
+
when :INT
|
108
|
+
handle_interrupt
|
109
|
+
when :HUP
|
110
|
+
handle_hangup
|
111
|
+
else
|
112
|
+
system "unhandled signal #{sig}"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# Handle a TERM signal
|
117
|
+
#
|
118
|
+
def handle_term_signal
|
119
|
+
@logger.warn "SIGTERM received"
|
120
|
+
terminate_gracefully
|
121
|
+
end
|
122
|
+
|
123
|
+
# Handle an INT signal
|
124
|
+
#
|
125
|
+
def handle_interrupt
|
126
|
+
@logger.warn "SIGINT received"
|
127
|
+
terminate_gracefully
|
128
|
+
end
|
129
|
+
|
130
|
+
# Handle a HUP signal
|
131
|
+
#
|
132
|
+
def handle_hangup
|
133
|
+
@logger.warn "SIGHUP received"
|
134
|
+
terminate_gracefully
|
135
|
+
end
|
136
|
+
|
137
|
+
def terminate_gracefully
|
138
|
+
return if @terminating
|
139
|
+
|
140
|
+
@terminating = true
|
141
|
+
|
142
|
+
stop
|
143
|
+
end
|
144
|
+
|
145
|
+
include MemoryMonitoring
|
146
|
+
|
56
147
|
def introspection
|
57
148
|
{
|
58
149
|
server: {
|
150
|
+
ruby_version: "#{RUBY_VERSION}p#{RUBY_PATCHLEVEL}",
|
59
151
|
rdkit_version: RDKit::VERSION,
|
60
152
|
multiplexing_api: 'select',
|
61
153
|
process_id: Process.pid,
|
@@ -65,6 +157,7 @@ module RDKit
|
|
65
157
|
hz: HZ,
|
66
158
|
},
|
67
159
|
clients: {
|
160
|
+
blocked_clients: @blocked_clients.size,
|
68
161
|
connected_clients: @clients.size,
|
69
162
|
connected_clients_peak: @peak_connected_clients
|
70
163
|
},
|
@@ -75,118 +168,181 @@ module RDKit
|
|
75
168
|
}
|
76
169
|
end
|
77
170
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
update_peak_memory!
|
171
|
+
def delete(socket)
|
172
|
+
@clients.delete(socket)
|
173
|
+
end
|
82
174
|
|
83
|
-
|
175
|
+
def clients
|
176
|
+
@clients.values
|
84
177
|
end
|
85
178
|
|
86
|
-
def
|
87
|
-
|
179
|
+
def select_db!(index)
|
180
|
+
if db = @all_dbs.find { |db| db.index == index }
|
181
|
+
@current_db = db
|
182
|
+
else
|
183
|
+
@all_dbs << DB.new(index)
|
184
|
+
|
185
|
+
@current_db = @all_dbs.last
|
186
|
+
end
|
88
187
|
end
|
89
188
|
|
90
|
-
def
|
91
|
-
|
189
|
+
def flushdb!
|
190
|
+
@current_db.flush!
|
191
|
+
end
|
92
192
|
|
93
|
-
|
193
|
+
def flushall!
|
194
|
+
flushdb!
|
94
195
|
|
95
|
-
@
|
196
|
+
@all_dbs = [@current_db]
|
197
|
+
end
|
96
198
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
end
|
101
|
-
end
|
199
|
+
def blocking(on_success=nil, &block)
|
200
|
+
@blocked_clients[current_client.socket] = current_client
|
201
|
+
@clients.delete(current_client.socket)
|
102
202
|
|
103
|
-
|
203
|
+
current_client.blocking(on_success, &block)
|
204
|
+
end
|
104
205
|
|
105
|
-
|
206
|
+
def pool
|
207
|
+
@pool ||= Thread.pool((ENV['RDKIT_SERVER_THREAD_POOL_SIZE'] || 10).to_i)
|
106
208
|
end
|
107
209
|
|
108
|
-
|
109
|
-
|
210
|
+
include Callbacks
|
211
|
+
|
212
|
+
private
|
213
|
+
|
214
|
+
def register_notification_observers!
|
215
|
+
if webhook = ENV['RDKIT_SLOW_LOG_BEARYCHAT_WEBHOOK']
|
216
|
+
require "httpi"
|
217
|
+
require "multi_json"
|
218
|
+
HTTPI.logger = Logger.new('/dev/null')
|
219
|
+
|
220
|
+
NotificationCenter.subscribe('slowlog', self) do |cmd, usec|
|
221
|
+
cmd, *args = cmd
|
110
222
|
|
111
|
-
|
112
|
-
|
223
|
+
text = "host=#{@host} port=#{@port} cmd=#{cmd}(#{args.join(',') }) usec=#{usec}"
|
224
|
+
|
225
|
+
pool.process { HTTPI.post(webhook, payload: MultiJson.dump({ text: text })) }
|
226
|
+
end
|
113
227
|
end
|
114
228
|
end
|
115
229
|
|
116
|
-
def
|
117
|
-
|
230
|
+
def sanity_check!
|
231
|
+
unless @host && @port
|
232
|
+
raise SDKRequirementNotMetError, '@host and @port are required for server to run'
|
233
|
+
end
|
234
|
+
|
235
|
+
if @core.nil?
|
236
|
+
raise SDKRequirementNotMetError, '@core is required to represent your business logics'
|
237
|
+
end
|
118
238
|
|
119
|
-
|
120
|
-
|
239
|
+
if responder.nil?
|
240
|
+
raise SDKRequirementNotMetError, '@responder is required to act as an RESP frontend'
|
241
|
+
end
|
121
242
|
|
122
|
-
|
123
|
-
|
243
|
+
if responder.server.nil?
|
244
|
+
raise SDKRequirementNotMetError, '@responder should have reference to server'
|
245
|
+
end
|
124
246
|
end
|
125
247
|
|
126
|
-
def
|
127
|
-
|
248
|
+
def add_client
|
249
|
+
Introspection::Stats.incr(:total_connections_received)
|
128
250
|
|
129
|
-
@
|
251
|
+
socket = @server_socket.accept_nonblock
|
130
252
|
|
131
|
-
|
132
|
-
|
253
|
+
client = @clients[socket] = Client.new(socket, self)
|
254
|
+
client.id = (@client_id_seq += 1)
|
133
255
|
|
134
|
-
|
256
|
+
@logger.debug "client #{socket} connected"
|
257
|
+
client_connected(client)
|
135
258
|
|
136
|
-
|
259
|
+
update_peak_connected_clients!
|
137
260
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
@
|
143
|
-
@
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
@logger.debug e
|
261
|
+
return @clients[socket]
|
262
|
+
end
|
263
|
+
|
264
|
+
def process(socket)
|
265
|
+
client = @clients[socket]
|
266
|
+
@current_client = client
|
267
|
+
client.resume
|
268
|
+
client_command_processed(client)
|
269
|
+
rescue ClientDisconnectedError => e
|
148
270
|
socket.close
|
149
|
-
@
|
150
|
-
|
271
|
+
@monitors.delete(client)
|
272
|
+
delete(socket)
|
273
|
+
client_disconnected(client)
|
151
274
|
end
|
152
275
|
|
153
276
|
def run_acceptor
|
154
|
-
@logger.info "accepting on shared socket (#{@host}:#{@port})"
|
277
|
+
@logger.info "accepting on shared socket (#{@host}:#{@port}), PID #{Process.pid}"
|
155
278
|
|
156
|
-
|
157
|
-
readable, _ = IO.select([@server_socket, @clients.keys].flatten, nil, nil, 1.0 / HZ)
|
158
|
-
|
159
|
-
if readable
|
160
|
-
readable.each do |socket|
|
161
|
-
if socket == @server_socket
|
162
|
-
add_client
|
163
|
-
else
|
164
|
-
# client is a Fiber
|
165
|
-
client = @clients[socket]
|
166
|
-
client.resume
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
279
|
+
server_started
|
170
280
|
|
171
|
-
|
172
|
-
|
281
|
+
loop do
|
282
|
+
process_blocked_clients
|
283
|
+
process_clients
|
173
284
|
|
174
285
|
@cycles += 1
|
175
286
|
|
176
287
|
core.tick!
|
288
|
+
|
289
|
+
gc_pool.process if @cycles % 1000 == 0
|
177
290
|
end
|
291
|
+
rescue Exception => e
|
292
|
+
@logger.warn e unless e.class == SystemExit
|
293
|
+
raise e
|
178
294
|
end
|
179
295
|
|
180
|
-
def
|
181
|
-
@
|
296
|
+
def gc_pool
|
297
|
+
@gc_pool ||= Thread.pool(1) do
|
298
|
+
_, usec = SlowLog.monitor('bg_gc') { GC.start }
|
299
|
+
|
300
|
+
Introspection::Commandstats.record('bg_gc', usec)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def process_blocked_clients
|
305
|
+
@blocked_clients.each do |socket, client|
|
306
|
+
if client.finished?
|
307
|
+
@clients[socket] = client
|
308
|
+
@blocked_clients.delete(socket)
|
309
|
+
|
310
|
+
client.unblock!
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
|
315
|
+
def process_clients
|
316
|
+
readable, _ = IO.select([@server_socket, @selfpipe[:reader], @clients.keys].flatten, nil, nil, 1.0 / HZ)
|
317
|
+
|
318
|
+
if readable
|
319
|
+
readable.each do |socket|
|
320
|
+
if socket == @server_socket
|
321
|
+
add_client
|
322
|
+
elsif socket == @selfpipe[:reader]
|
323
|
+
handle_signals
|
324
|
+
else
|
325
|
+
process(socket)
|
326
|
+
end
|
327
|
+
|
328
|
+
process_blocked_clients
|
329
|
+
end
|
330
|
+
end
|
182
331
|
end
|
183
332
|
|
184
333
|
def update_peak_connected_clients!
|
185
334
|
@peak_connected_clients = [@peak_connected_clients, @clients.size].max
|
186
335
|
end
|
187
336
|
|
188
|
-
|
189
|
-
|
337
|
+
module ClassMethods
|
338
|
+
def register(instance)
|
339
|
+
@@instance = instance
|
340
|
+
end
|
341
|
+
|
342
|
+
def instance
|
343
|
+
@@instance
|
344
|
+
end
|
190
345
|
end
|
346
|
+
class << self; include ClassMethods; end
|
191
347
|
end
|
192
348
|
end
|