kino 0.3.0 → 0.5.0
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/CHANGELOG.md +55 -0
- data/Cargo.lock +154 -25
- data/README.md +71 -14
- data/doc/architecture.md +7 -4
- data/exe/kino +0 -4
- data/ext/kino/Cargo.toml +4 -2
- data/ext/kino/src/access_log.rs +245 -0
- data/ext/kino/src/control.rs +279 -89
- data/ext/kino/src/cpus.rs +37 -0
- data/ext/kino/src/env_strings.rs +10 -2
- data/ext/kino/src/io_shards.rs +437 -0
- data/ext/kino/src/lib.rs +11 -1
- data/ext/kino/src/listen.rs +137 -0
- data/ext/kino/src/log.rs +152 -0
- data/ext/kino/src/queue.rs +9 -4
- data/ext/kino/src/registry.rs +102 -10
- data/ext/kino/src/request.rs +48 -2
- data/ext/kino/src/response.rs +1 -4
- data/ext/kino/src/server.rs +207 -95
- data/ext/kino/src/style.rs +42 -39
- data/ext/kino/src/tls.rs +11 -4
- data/lib/kino/cli.rb +20 -9
- data/lib/kino/configuration.rb +30 -5
- data/lib/kino/errors_stream.rb +4 -3
- data/lib/kino/hook_fire.rb +3 -4
- data/lib/kino/log.rb +104 -0
- data/lib/kino/quarantine_monitor.rb +7 -4
- data/lib/kino/ractor_supervisor.rb +8 -3
- data/lib/kino/server.rb +68 -15
- data/lib/kino/templates/kino.rb.tt +19 -7
- data/lib/kino/version.rb +1 -1
- data/lib/kino/worker.rb +12 -13
- data/lib/kino/worker_hooks.rb +3 -1
- data/lib/kino.rb +11 -0
- data/lib/rackup/handler/kino.rb +88 -0
- data/sig/kino.rbs +64 -1
- metadata +22 -1
data/lib/kino.rb
CHANGED
|
@@ -33,9 +33,20 @@ module Kino
|
|
|
33
33
|
remaining = Native.sleep_chunk(remaining) while remaining.positive?
|
|
34
34
|
nil
|
|
35
35
|
end
|
|
36
|
+
|
|
37
|
+
# How many CPUs this process may actually use: the `workers` default.
|
|
38
|
+
# Unlike `Etc.nprocessors`, this honours a cgroup CPU quota (a container
|
|
39
|
+
# limited to 2 CPUs on a 64-core host gets 2, not 64) as well as the
|
|
40
|
+
# affinity mask; a fractional quota rounds up. Never below 1.
|
|
41
|
+
#
|
|
42
|
+
# @return [Integer]
|
|
43
|
+
def self.available_parallelism
|
|
44
|
+
Native.available_parallelism
|
|
45
|
+
end
|
|
36
46
|
end
|
|
37
47
|
|
|
38
48
|
require_relative "kino/cli"
|
|
49
|
+
require_relative "kino/log"
|
|
39
50
|
require_relative "kino/logger"
|
|
40
51
|
require_relative "kino/check"
|
|
41
52
|
require_relative "kino/input"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rackup
|
|
4
|
+
module Handler
|
|
5
|
+
# The Rack handler: lets any host that speaks the rackup protocol boot
|
|
6
|
+
# Kino, which is what `rackup -s kino` and `rails server -u kino` do.
|
|
7
|
+
# Loaded on demand by Rackup::Handler.get(:kino), so Rackup itself is
|
|
8
|
+
# already defined here; Kino is required only when the host calls in.
|
|
9
|
+
module Kino
|
|
10
|
+
# Host option name => Kino setting plus the coercion it needs: rackup
|
|
11
|
+
# hands `-O NAME=VALUE` values (and its own -p) over as strings.
|
|
12
|
+
OPTION_MAP = {
|
|
13
|
+
Host: [:bind, ->(value) { value.to_s }],
|
|
14
|
+
Port: [:port, ->(value) { Integer(value) }],
|
|
15
|
+
Workers: [:workers, ->(value) { Integer(value) }],
|
|
16
|
+
Threads: [:threads, ->(value) { Integer(value) }],
|
|
17
|
+
Mode: [:mode, ->(value) { value.to_sym }]
|
|
18
|
+
}.freeze
|
|
19
|
+
private_constant :OPTION_MAP
|
|
20
|
+
|
|
21
|
+
# Boot a server for `app` and block until it shuts down, the way the
|
|
22
|
+
# `kino` executable does (banner, INT/TERM drain, stats on USR1).
|
|
23
|
+
#
|
|
24
|
+
# @param app [#call] the Rack application the host built
|
|
25
|
+
# @param options [Hash] the host's options (see {.server_options})
|
|
26
|
+
# @yield [server] the built, not yet started server, for hosts that
|
|
27
|
+
# want a handle on it
|
|
28
|
+
# @return [::Kino::Server] the stopped server, after shutdown
|
|
29
|
+
def self.run(app, **options)
|
|
30
|
+
require "kino"
|
|
31
|
+
server = ::Kino::Server.new(app, **server_options(options))
|
|
32
|
+
yield server if block_given?
|
|
33
|
+
server.run
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The `-O NAME=VALUE` options `rackup -s kino --help` lists (rackup
|
|
37
|
+
# shows its own -o/-p in place of Host and Port).
|
|
38
|
+
# @return [Hash{String => String}]
|
|
39
|
+
def self.valid_options
|
|
40
|
+
{
|
|
41
|
+
"Host=HOST" => "Address to bind (default: 127.0.0.1)",
|
|
42
|
+
"Port=PORT" => "Port to listen on (default: 9292)",
|
|
43
|
+
"Workers=COUNT" => "Workers: ractors in :ractor mode, thread groups in :threaded (default: one per CPU)",
|
|
44
|
+
"Threads=COUNT" => "Threads per worker (default: 1 in :ractor, 3 in :threaded)",
|
|
45
|
+
"Mode=MODE" => "auto | ractor | threaded (default: auto)",
|
|
46
|
+
"Config=PATH" => "Kino config file (default: kino.rb, then config/kino.rb)"
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Translate host options into {::Kino::Server#initialize} kwargs.
|
|
51
|
+
# Precedence: options the user typed > the config file > defaults the
|
|
52
|
+
# host supplied (rackup's and Rails' own Host and Port) > Kino's
|
|
53
|
+
# defaults. Hosts that say which options were typed pass
|
|
54
|
+
# `user_supplied_options`; when that list is absent every option
|
|
55
|
+
# counts as typed. Keys outside OPTION_MAP (the host's bookkeeping:
|
|
56
|
+
# environment, pid, config, ...) are ignored.
|
|
57
|
+
#
|
|
58
|
+
# @param options [Hash{Symbol => Object}]
|
|
59
|
+
# @return [Hash{Symbol => Object}]
|
|
60
|
+
def self.server_options(options)
|
|
61
|
+
require "kino"
|
|
62
|
+
options = options.dup
|
|
63
|
+
host_defaults = {}
|
|
64
|
+
if (typed = options.delete(:user_supplied_options))
|
|
65
|
+
(options.keys - typed).each { |key| host_defaults[key] = options.delete(key) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
config = ::Kino::Configuration.new
|
|
69
|
+
path = options.delete(:Config) || host_defaults.delete(:Config) || ::Kino::Configuration.default_path
|
|
70
|
+
config.load_file(path) if path
|
|
71
|
+
translate(host_defaults).each { |key, value| config.set(key, value) unless config.set?(key) }
|
|
72
|
+
config.merge!(translate(options))
|
|
73
|
+
config.set(:port, ::Kino::Configuration::DEFAULT_SERVING_PORT) unless config.set?(:port)
|
|
74
|
+
config.server_options
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def self.translate(options)
|
|
78
|
+
options.filter_map do |key, value|
|
|
79
|
+
setting, coerce = OPTION_MAP[key]
|
|
80
|
+
[setting, coerce.call(value)] if setting
|
|
81
|
+
end.to_h
|
|
82
|
+
end
|
|
83
|
+
private_class_method :translate
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
register :kino, Kino
|
|
87
|
+
end
|
|
88
|
+
end
|
data/sig/kino.rbs
CHANGED
|
@@ -20,6 +20,9 @@ module Kino
|
|
|
20
20
|
# High-resolution sleep on the OS clock with the GVL released.
|
|
21
21
|
def self.sleep: (Numeric seconds) -> nil
|
|
22
22
|
|
|
23
|
+
# CPUs this process may use (affinity mask and cgroup quota); never below 1.
|
|
24
|
+
def self.available_parallelism: () -> Integer
|
|
25
|
+
|
|
23
26
|
class Server
|
|
24
27
|
attr_reader port: Integer?
|
|
25
28
|
attr_reader control_port: Integer?
|
|
@@ -28,6 +31,16 @@ module Kino
|
|
|
28
31
|
|
|
29
32
|
def tls?: () -> bool
|
|
30
33
|
|
|
34
|
+
# Whether the bind is a unix domain socket ("unix:///path/to.sock").
|
|
35
|
+
def unix?: () -> bool
|
|
36
|
+
|
|
37
|
+
# Where the server listens once started: http(s)://host:port or the
|
|
38
|
+
# unix:// socket path.
|
|
39
|
+
def url: () -> String
|
|
40
|
+
|
|
41
|
+
# Where the control plane listens once started, or nil when it is off.
|
|
42
|
+
def control_url: () -> String?
|
|
43
|
+
|
|
31
44
|
# Settings precedence: explicit kwargs > config_file DSL > defaults.
|
|
32
45
|
def initialize: (rack_app app, ?config_file: String?, **untyped options) -> void
|
|
33
46
|
|
|
@@ -42,9 +55,12 @@ module Kino
|
|
|
42
55
|
# served, rejected, timeouts, respawns, lane_depths when lanes are on).
|
|
43
56
|
def stats: () -> stats_hash
|
|
44
57
|
|
|
45
|
-
# Production entry point:
|
|
58
|
+
# Production entry point: build the server and #run it.
|
|
46
59
|
def self.run: (rack_app app, **untyped opts) -> Server
|
|
47
60
|
|
|
61
|
+
# Serve until shut down: start, banner, signal traps, block until done.
|
|
62
|
+
def run: () -> self
|
|
63
|
+
|
|
48
64
|
# INT/TERM drain gracefully (second signal force-exits); USR1 prints stats.
|
|
49
65
|
def self.trap_signals: (Server server) -> void
|
|
50
66
|
end
|
|
@@ -53,6 +69,12 @@ module Kino
|
|
|
53
69
|
DEFAULTS: Hash[Symbol, untyped]
|
|
54
70
|
SETTINGS: Array[Symbol]
|
|
55
71
|
SAMPLE_TEMPLATE: String
|
|
72
|
+
DEFAULT_PATHS: Array[String]
|
|
73
|
+
DEFAULT_SERVING_PORT: Integer
|
|
74
|
+
|
|
75
|
+
# The first of DEFAULT_PATHS (kino.rb, config/kino.rb) present in the
|
|
76
|
+
# working directory.
|
|
77
|
+
def self.default_path: () -> String?
|
|
56
78
|
|
|
57
79
|
# The fully commented sample config (see `kino --init`).
|
|
58
80
|
def self.sample: () -> String
|
|
@@ -104,6 +126,8 @@ module Kino
|
|
|
104
126
|
def after_request_complete: (?^(Hash[String, untyped], Integer) -> void handler) ?{ (Hash[String, untyped], Integer) -> void } -> untyped
|
|
105
127
|
def on_worker_exit: (?^(Integer, Exception?) -> void handler) ?{ (Integer, Exception?) -> void } -> untyped
|
|
106
128
|
def shutdown_timeout: (Numeric seconds) -> untyped
|
|
129
|
+
def io_shards: (?boolish enabled) -> untyped
|
|
130
|
+
def io_threads: (int? count) -> untyped
|
|
107
131
|
def tokio_threads: (int count) -> untyped
|
|
108
132
|
def tls: (cert: String, key: String) -> untyped
|
|
109
133
|
def environment: (String | Symbol env) -> untyped
|
|
@@ -168,6 +192,27 @@ module Kino
|
|
|
168
192
|
def self.print_report: (rack_app app, ?io: IO) -> bool
|
|
169
193
|
end
|
|
170
194
|
|
|
195
|
+
# Server log lines, `kino[<pid>] <source>: message`; safe inside worker
|
|
196
|
+
# ractors, so hooks may log through it.
|
|
197
|
+
module Log
|
|
198
|
+
FRAMES: Integer
|
|
199
|
+
WORKING_DIR: String
|
|
200
|
+
|
|
201
|
+
def self.info: (untyped message) -> void
|
|
202
|
+
def self.warn: (untyped message) -> void
|
|
203
|
+
def self.error: (untyped message) -> void
|
|
204
|
+
|
|
205
|
+
# The failed-request report: request line, error, and an app-first
|
|
206
|
+
# backtrace relative to the working directory.
|
|
207
|
+
def self.exception: (Exception error, Hash[String, untyped] env, ?status: Integer) -> void
|
|
208
|
+
|
|
209
|
+
# The `kino[<pid>] <source>:` tag.
|
|
210
|
+
def self.label: () -> String
|
|
211
|
+
|
|
212
|
+
# The ractor and/or thread name, or `main`.
|
|
213
|
+
def self.source: () -> String
|
|
214
|
+
end
|
|
215
|
+
|
|
171
216
|
# A ::Logger writing through the native async sink.
|
|
172
217
|
class Logger < ::Logger
|
|
173
218
|
# path: a file (created/appended) or nil for stdout.
|
|
@@ -188,3 +233,21 @@ module Kino
|
|
|
188
233
|
end
|
|
189
234
|
end
|
|
190
235
|
end
|
|
236
|
+
|
|
237
|
+
module Rackup
|
|
238
|
+
module Handler
|
|
239
|
+
# The Rack handler behind `rackup -s kino` and `rails server -u kino`.
|
|
240
|
+
module Kino
|
|
241
|
+
# Boot a server for app and block until shutdown; yields the built,
|
|
242
|
+
# not yet started server to hosts that want a handle on it.
|
|
243
|
+
def self.run: (::Kino::rack_app app, **untyped options) ?{ (::Kino::Server) -> void } -> ::Kino::Server
|
|
244
|
+
|
|
245
|
+
# The -O options rackup lists for this handler.
|
|
246
|
+
def self.valid_options: () -> Hash[String, String]
|
|
247
|
+
|
|
248
|
+
# Host options translated into Kino::Server kwargs, with precedence
|
|
249
|
+
# typed > config file > host defaults > Kino defaults.
|
|
250
|
+
def self.server_options: (Hash[Symbol, untyped] options) -> Hash[Symbol, untyped]
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kino
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yaroslav Markin
|
|
@@ -51,6 +51,20 @@ dependencies:
|
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
53
|
version: 0.9.128
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rackup
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '2.2'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '2.2'
|
|
54
68
|
- !ruby/object:Gem::Dependency
|
|
55
69
|
name: rake
|
|
56
70
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -161,10 +175,15 @@ files:
|
|
|
161
175
|
- ext/kino/Cargo.toml
|
|
162
176
|
- ext/kino/build.rs
|
|
163
177
|
- ext/kino/extconf.rb
|
|
178
|
+
- ext/kino/src/access_log.rs
|
|
164
179
|
- ext/kino/src/control.rs
|
|
180
|
+
- ext/kino/src/cpus.rs
|
|
165
181
|
- ext/kino/src/env_strings.rs
|
|
166
182
|
- ext/kino/src/gvl.rs
|
|
183
|
+
- ext/kino/src/io_shards.rs
|
|
167
184
|
- ext/kino/src/lib.rs
|
|
185
|
+
- ext/kino/src/listen.rs
|
|
186
|
+
- ext/kino/src/log.rs
|
|
168
187
|
- ext/kino/src/logsink.rs
|
|
169
188
|
- ext/kino/src/mono.rs
|
|
170
189
|
- ext/kino/src/pin.rs
|
|
@@ -184,6 +203,7 @@ files:
|
|
|
184
203
|
- lib/kino/errors_stream.rb
|
|
185
204
|
- lib/kino/hook_fire.rb
|
|
186
205
|
- lib/kino/input.rb
|
|
206
|
+
- lib/kino/log.rb
|
|
187
207
|
- lib/kino/logger.rb
|
|
188
208
|
- lib/kino/null_input.rb
|
|
189
209
|
- lib/kino/quarantine_monitor.rb
|
|
@@ -194,6 +214,7 @@ files:
|
|
|
194
214
|
- lib/kino/version.rb
|
|
195
215
|
- lib/kino/worker.rb
|
|
196
216
|
- lib/kino/worker_hooks.rb
|
|
217
|
+
- lib/rackup/handler/kino.rb
|
|
197
218
|
- sig/kino.rbs
|
|
198
219
|
homepage: https://github.com/yaroslav/kino
|
|
199
220
|
licenses:
|