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.
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: start, banner, signal traps, block until done.
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.3.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: