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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 19a396c4f03ec64ac1f598ca8563c69122a0db0a543372c629a56e3ed88d8d06
|
|
4
|
+
data.tar.gz: 57c185c402a0f89d0b177c0c017d377abc5fbe8effff999401ae3196426bd6c2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c208de3731186a58be3c4f0ca4f6795fae98405133f0d954ff105a3353892769842cc10bc11fe72dabcaa33c275218e2154eac839cef2fdf729d7959e886429f
|
|
7
|
+
data.tar.gz: 549bd8c04aa00b3e2c799d717a8d5bbeb702530a81ea688948b04535b848f1eb03c8e0f52fc1dca362189a00b01269c06ff1d5993e6f2de8e6d0f1e322132f4b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
## [0.5.0] - 2026-08-29
|
|
2
|
+
|
|
3
|
+
- Add opt-in `io_shards true`: accepted HTTP connections can run on
|
|
4
|
+
current-thread Tokio I/O shards instead of Tokio's shared multi-thread
|
|
5
|
+
runtime, reducing scheduler contention on very fast handlers. `io_threads`
|
|
6
|
+
sets the shard count; otherwise Kino uses half the available CPUs
|
|
7
|
+
([Patrik Wenger](https://github.com/paddor)).
|
|
8
|
+
- Update Rust dependencies: hyper 1.11.1, rb-sys 0.9.130,
|
|
9
|
+
rustls-webpki 0.103.15, among others.
|
|
10
|
+
|
|
11
|
+
## [0.4.0] - 2026-08-22
|
|
12
|
+
|
|
13
|
+
- Rack handler: `rails server -u kino` and `rackup -s kino` boot Kino
|
|
14
|
+
through `Rackup::Handler::Kino`, reading the same config file as the
|
|
15
|
+
`kino` CLI with the host's flags on top; `rackup -s kino --help` lists
|
|
16
|
+
the `-O` options (Workers, Threads, Mode, Config).
|
|
17
|
+
- The config file is also looked up at `config/kino.rb` (the Rails
|
|
18
|
+
layout) when there is no `kino.rb`, by the CLI and the handler alike.
|
|
19
|
+
- `Kino::Server#run` serves an already built server the way
|
|
20
|
+
`Kino::Server.run` does (banner, signal traps, block until shutdown),
|
|
21
|
+
and syncs stdout there so the banner is never held back by block
|
|
22
|
+
buffering under a pipe, whichever entry point booted the server.
|
|
23
|
+
- `workers` now defaults to `Kino.available_parallelism`, the CPUs the
|
|
24
|
+
process may actually use: the affinity mask and, in a container, the
|
|
25
|
+
cgroup CPU quota (a pod limited to 2 CPUs on a 64-core node gets 2
|
|
26
|
+
workers, not 64). `Etc.nprocessors` only ever saw the mask.
|
|
27
|
+
- `bind "unix:///path/to.sock"` listens on a unix domain socket, the
|
|
28
|
+
usual shape behind nginx: a stale socket file is reclaimed, a live one
|
|
29
|
+
is refused, and the file is removed on shutdown. `port` is unused on
|
|
30
|
+
it and TLS is rejected (terminate TLS at the proxy). Requests arriving
|
|
31
|
+
over the socket report `REMOTE_ADDR` 127.0.0.1.
|
|
32
|
+
- `Kino::Server#url`, `#control_url`, and `#unix?` report where a started
|
|
33
|
+
server and its control plane listen.
|
|
34
|
+
- The access log is two records per request: an arrival line queued
|
|
35
|
+
before the app runs (a hang shows as an arrow with no answer) and a
|
|
36
|
+
status-tinted completion line with a timing breakdown of `ruby`,
|
|
37
|
+
`kino`, and `wait`, the `ruby` part carrying the GC pause and objects
|
|
38
|
+
allocated where one request at a time can own the VM's counters
|
|
39
|
+
(`:threaded`, or `:ractor` with `workers 1`). Local timestamps with
|
|
40
|
+
their UTC offset; a blank line between requests. The former one-line
|
|
41
|
+
format is gone.
|
|
42
|
+
- A failed request is reported as `500 GET /path · Class: message (site)`
|
|
43
|
+
followed by its backtrace relative to the working directory, the app's
|
|
44
|
+
own frames first, the rest folded into `… N more`.
|
|
45
|
+
- Every line Kino logs about itself (draining, a crash and its respawn,
|
|
46
|
+
hook failures, quarantine, the USR1 stats line, `rack.errors`) reads
|
|
47
|
+
`kino[<pid>] <source>: message`, the source naming the worker that
|
|
48
|
+
spoke (`worker-3`, `worker-3/thread-2`) or `main`; worker ractors and
|
|
49
|
+
threads now carry those names. The label is dim, yellow, or red by
|
|
50
|
+
level on color terminals. `Kino::Log.info`, `.warn`, and `.error` are
|
|
51
|
+
public, for hooks.
|
|
52
|
+
- The startup banner lists the Ruby build with its JIT and parser flags,
|
|
53
|
+
the environment, the topology, the pid, and the control-plane address
|
|
54
|
+
when one is bound.
|
|
55
|
+
|
|
1
56
|
## [0.3.0] - 2026-08-13
|
|
2
57
|
|
|
3
58
|
- Queue-time histogram: `/metrics` exposes `kino_request_queue_seconds`, a
|
data/Cargo.lock
CHANGED
|
@@ -42,7 +42,7 @@ version = "0.72.1"
|
|
|
42
42
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
43
43
|
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
|
|
44
44
|
dependencies = [
|
|
45
|
-
"bitflags",
|
|
45
|
+
"bitflags 2.13.1",
|
|
46
46
|
"cexpr",
|
|
47
47
|
"clang-sys",
|
|
48
48
|
"itertools",
|
|
@@ -54,6 +54,12 @@ dependencies = [
|
|
|
54
54
|
"syn 2.0.119",
|
|
55
55
|
]
|
|
56
56
|
|
|
57
|
+
[[package]]
|
|
58
|
+
name = "bitflags"
|
|
59
|
+
version = "1.3.2"
|
|
60
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
61
|
+
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
62
|
+
|
|
57
63
|
[[package]]
|
|
58
64
|
name = "bitflags"
|
|
59
65
|
version = "2.13.1"
|
|
@@ -74,9 +80,9 @@ checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04"
|
|
|
74
80
|
|
|
75
81
|
[[package]]
|
|
76
82
|
name = "cc"
|
|
77
|
-
version = "1.4.
|
|
83
|
+
version = "1.4.4"
|
|
78
84
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
79
|
-
checksum = "
|
|
85
|
+
checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273"
|
|
80
86
|
dependencies = [
|
|
81
87
|
"find-msvc-tools",
|
|
82
88
|
"shlex 2.0.1",
|
|
@@ -108,11 +114,42 @@ dependencies = [
|
|
|
108
114
|
"libloading",
|
|
109
115
|
]
|
|
110
116
|
|
|
117
|
+
[[package]]
|
|
118
|
+
name = "defmt"
|
|
119
|
+
version = "1.1.1"
|
|
120
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
121
|
+
checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1"
|
|
122
|
+
dependencies = [
|
|
123
|
+
"bitflags 1.3.2",
|
|
124
|
+
"defmt-macros",
|
|
125
|
+
]
|
|
126
|
+
|
|
127
|
+
[[package]]
|
|
128
|
+
name = "defmt-macros"
|
|
129
|
+
version = "1.1.1"
|
|
130
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
131
|
+
checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8"
|
|
132
|
+
dependencies = [
|
|
133
|
+
"defmt-parser",
|
|
134
|
+
"proc-macro2",
|
|
135
|
+
"quote",
|
|
136
|
+
"syn 2.0.119",
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
[[package]]
|
|
140
|
+
name = "defmt-parser"
|
|
141
|
+
version = "1.0.0"
|
|
142
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
143
|
+
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
|
|
144
|
+
dependencies = [
|
|
145
|
+
"thiserror",
|
|
146
|
+
]
|
|
147
|
+
|
|
111
148
|
[[package]]
|
|
112
149
|
name = "either"
|
|
113
|
-
version = "1.
|
|
150
|
+
version = "1.18.0"
|
|
114
151
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
115
|
-
checksum = "
|
|
152
|
+
checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
|
|
116
153
|
|
|
117
154
|
[[package]]
|
|
118
155
|
name = "equivalent"
|
|
@@ -131,9 +168,9 @@ dependencies = [
|
|
|
131
168
|
|
|
132
169
|
[[package]]
|
|
133
170
|
name = "find-msvc-tools"
|
|
134
|
-
version = "0.1.
|
|
171
|
+
version = "0.1.11"
|
|
135
172
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
136
|
-
checksum = "
|
|
173
|
+
checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
|
|
137
174
|
|
|
138
175
|
[[package]]
|
|
139
176
|
name = "flume"
|
|
@@ -274,9 +311,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|
|
274
311
|
|
|
275
312
|
[[package]]
|
|
276
313
|
name = "hyper"
|
|
277
|
-
version = "1.11.
|
|
314
|
+
version = "1.11.1"
|
|
278
315
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
279
|
-
checksum = "
|
|
316
|
+
checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43"
|
|
280
317
|
dependencies = [
|
|
281
318
|
"atomic-waker",
|
|
282
319
|
"bytes",
|
|
@@ -321,6 +358,43 @@ version = "1.0.18"
|
|
|
321
358
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
322
359
|
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
323
360
|
|
|
361
|
+
[[package]]
|
|
362
|
+
name = "jiff"
|
|
363
|
+
version = "0.2.35"
|
|
364
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
365
|
+
checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc"
|
|
366
|
+
dependencies = [
|
|
367
|
+
"defmt",
|
|
368
|
+
"jiff-core",
|
|
369
|
+
"jiff-static",
|
|
370
|
+
"log",
|
|
371
|
+
"portable-atomic",
|
|
372
|
+
"portable-atomic-util",
|
|
373
|
+
"serde_core",
|
|
374
|
+
"windows-link",
|
|
375
|
+
]
|
|
376
|
+
|
|
377
|
+
[[package]]
|
|
378
|
+
name = "jiff-core"
|
|
379
|
+
version = "0.1.0"
|
|
380
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
381
|
+
checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09"
|
|
382
|
+
dependencies = [
|
|
383
|
+
"defmt",
|
|
384
|
+
]
|
|
385
|
+
|
|
386
|
+
[[package]]
|
|
387
|
+
name = "jiff-static"
|
|
388
|
+
version = "0.2.35"
|
|
389
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
390
|
+
checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204"
|
|
391
|
+
dependencies = [
|
|
392
|
+
"jiff-core",
|
|
393
|
+
"proc-macro2",
|
|
394
|
+
"quote",
|
|
395
|
+
"syn 2.0.119",
|
|
396
|
+
]
|
|
397
|
+
|
|
324
398
|
[[package]]
|
|
325
399
|
name = "js-sys"
|
|
326
400
|
version = "0.3.104"
|
|
@@ -333,16 +407,16 @@ dependencies = [
|
|
|
333
407
|
|
|
334
408
|
[[package]]
|
|
335
409
|
name = "kino"
|
|
336
|
-
version = "0.
|
|
410
|
+
version = "0.5.0"
|
|
337
411
|
dependencies = [
|
|
338
412
|
"ahash",
|
|
339
413
|
"bytes",
|
|
340
414
|
"flume",
|
|
341
415
|
"http",
|
|
342
416
|
"http-body-util",
|
|
343
|
-
"httpdate",
|
|
344
417
|
"hyper",
|
|
345
418
|
"hyper-util",
|
|
419
|
+
"jiff",
|
|
346
420
|
"lru",
|
|
347
421
|
"magnus",
|
|
348
422
|
"mimalloc",
|
|
@@ -397,15 +471,15 @@ dependencies = [
|
|
|
397
471
|
|
|
398
472
|
[[package]]
|
|
399
473
|
name = "log"
|
|
400
|
-
version = "0.4.
|
|
474
|
+
version = "0.4.34"
|
|
401
475
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
402
|
-
checksum = "
|
|
476
|
+
checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
|
|
403
477
|
|
|
404
478
|
[[package]]
|
|
405
479
|
name = "lru"
|
|
406
|
-
version = "0.18.
|
|
480
|
+
version = "0.18.3"
|
|
407
481
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
408
|
-
checksum = "
|
|
482
|
+
checksum = "0d317b4b9eb398e6acce275758ec6125535505e7a146fb1a9b8bda2451b0ff4c"
|
|
409
483
|
dependencies = [
|
|
410
484
|
"hashbrown",
|
|
411
485
|
]
|
|
@@ -510,6 +584,21 @@ version = "0.2.17"
|
|
|
510
584
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
511
585
|
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
|
512
586
|
|
|
587
|
+
[[package]]
|
|
588
|
+
name = "portable-atomic"
|
|
589
|
+
version = "1.15.0"
|
|
590
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
591
|
+
checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85"
|
|
592
|
+
|
|
593
|
+
[[package]]
|
|
594
|
+
name = "portable-atomic-util"
|
|
595
|
+
version = "0.2.7"
|
|
596
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
597
|
+
checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618"
|
|
598
|
+
dependencies = [
|
|
599
|
+
"portable-atomic",
|
|
600
|
+
]
|
|
601
|
+
|
|
513
602
|
[[package]]
|
|
514
603
|
name = "proc-macro2"
|
|
515
604
|
version = "1.0.107"
|
|
@@ -542,18 +631,18 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
|
|
|
542
631
|
|
|
543
632
|
[[package]]
|
|
544
633
|
name = "rb-sys"
|
|
545
|
-
version = "0.9.
|
|
634
|
+
version = "0.9.130"
|
|
546
635
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
547
|
-
checksum = "
|
|
636
|
+
checksum = "02faf625bb10ba893e3ae620f19c9fb1b5f8fcae0fe4eb86bb3f2230fad75edb"
|
|
548
637
|
dependencies = [
|
|
549
638
|
"rb-sys-build",
|
|
550
639
|
]
|
|
551
640
|
|
|
552
641
|
[[package]]
|
|
553
642
|
name = "rb-sys-build"
|
|
554
|
-
version = "0.9.
|
|
643
|
+
version = "0.9.130"
|
|
555
644
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
556
|
-
checksum = "
|
|
645
|
+
checksum = "05be6f9c86fe5482808162826f6c047d093b3670582296c770de1d94f8066694"
|
|
557
646
|
dependencies = [
|
|
558
647
|
"bindgen",
|
|
559
648
|
"lazy_static",
|
|
@@ -576,7 +665,7 @@ version = "0.5.18"
|
|
|
576
665
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
577
666
|
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
|
|
578
667
|
dependencies = [
|
|
579
|
-
"bitflags",
|
|
668
|
+
"bitflags 2.13.1",
|
|
580
669
|
]
|
|
581
670
|
|
|
582
671
|
[[package]]
|
|
@@ -663,9 +752,9 @@ dependencies = [
|
|
|
663
752
|
|
|
664
753
|
[[package]]
|
|
665
754
|
name = "rustls-webpki"
|
|
666
|
-
version = "0.103.
|
|
755
|
+
version = "0.103.15"
|
|
667
756
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
668
|
-
checksum = "
|
|
757
|
+
checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
|
|
669
758
|
dependencies = [
|
|
670
759
|
"ring",
|
|
671
760
|
"rustls-pki-types",
|
|
@@ -690,6 +779,26 @@ version = "0.3.6"
|
|
|
690
779
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
691
780
|
checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc"
|
|
692
781
|
|
|
782
|
+
[[package]]
|
|
783
|
+
name = "serde_core"
|
|
784
|
+
version = "1.0.229"
|
|
785
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
786
|
+
checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
|
|
787
|
+
dependencies = [
|
|
788
|
+
"serde_derive",
|
|
789
|
+
]
|
|
790
|
+
|
|
791
|
+
[[package]]
|
|
792
|
+
name = "serde_derive"
|
|
793
|
+
version = "1.0.229"
|
|
794
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
795
|
+
checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
|
|
796
|
+
dependencies = [
|
|
797
|
+
"proc-macro2",
|
|
798
|
+
"quote",
|
|
799
|
+
"syn 3.0.4",
|
|
800
|
+
]
|
|
801
|
+
|
|
693
802
|
[[package]]
|
|
694
803
|
name = "shell-words"
|
|
695
804
|
version = "1.1.1"
|
|
@@ -752,15 +861,35 @@ dependencies = [
|
|
|
752
861
|
|
|
753
862
|
[[package]]
|
|
754
863
|
name = "syn"
|
|
755
|
-
version = "3.0.
|
|
864
|
+
version = "3.0.4"
|
|
756
865
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
757
|
-
checksum = "
|
|
866
|
+
checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
|
|
758
867
|
dependencies = [
|
|
759
868
|
"proc-macro2",
|
|
760
869
|
"quote",
|
|
761
870
|
"unicode-ident",
|
|
762
871
|
]
|
|
763
872
|
|
|
873
|
+
[[package]]
|
|
874
|
+
name = "thiserror"
|
|
875
|
+
version = "2.0.20"
|
|
876
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
877
|
+
checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f"
|
|
878
|
+
dependencies = [
|
|
879
|
+
"thiserror-impl",
|
|
880
|
+
]
|
|
881
|
+
|
|
882
|
+
[[package]]
|
|
883
|
+
name = "thiserror-impl"
|
|
884
|
+
version = "2.0.20"
|
|
885
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
886
|
+
checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
|
|
887
|
+
dependencies = [
|
|
888
|
+
"proc-macro2",
|
|
889
|
+
"quote",
|
|
890
|
+
"syn 3.0.4",
|
|
891
|
+
]
|
|
892
|
+
|
|
764
893
|
[[package]]
|
|
765
894
|
name = "tokio"
|
|
766
895
|
version = "1.53.1"
|
|
@@ -784,7 +913,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
|
|
|
784
913
|
dependencies = [
|
|
785
914
|
"proc-macro2",
|
|
786
915
|
"quote",
|
|
787
|
-
"syn 3.0.
|
|
916
|
+
"syn 3.0.4",
|
|
788
917
|
]
|
|
789
918
|
|
|
790
919
|
[[package]]
|
data/README.md
CHANGED
|
@@ -196,6 +196,12 @@ bundle exec kino # picks up config.ru + kino.rb, serves on :9292
|
|
|
196
196
|
(After a standalone `gem install`, the `kino` command works without
|
|
197
197
|
`bundle exec`.)
|
|
198
198
|
|
|
199
|
+
Prefer your framework's own command? Kino ships a Rack handler, so
|
|
200
|
+
`rails server -u kino` and `rackup -s kino` boot it too. They read the
|
|
201
|
+
same `kino.rb` (or `config/kino.rb`), the host's `-p`/`-b` flags win over
|
|
202
|
+
the file, and `rackup -s kino -O Workers=4 -O Mode=threaded` reaches the
|
|
203
|
+
rest (`rackup -s kino --help` lists them).
|
|
204
|
+
|
|
199
205
|
No Rust compiler needed: released versions ship precompiled native gems
|
|
200
206
|
for Linux (x86_64/aarch64, glibc and musl) and macOS (arm64). On other
|
|
201
207
|
platforms the gem compiles at install time; that needs a Rust toolchain,
|
|
@@ -218,9 +224,9 @@ Or embedded, with everything spelled out:
|
|
|
218
224
|
|
|
219
225
|
```ruby
|
|
220
226
|
server = Kino::Server.new(app,
|
|
221
|
-
bind: "127.0.0.1",
|
|
227
|
+
bind: "127.0.0.1", # or "unix:///run/kino.sock" behind a proxy
|
|
222
228
|
port: 9292, # 0 = ephemeral; read back via server.port
|
|
223
|
-
workers:
|
|
229
|
+
workers: Kino.available_parallelism, # ractors (parallelism); the default
|
|
224
230
|
threads: 1, # per worker; ractor default 1, threaded default 3
|
|
225
231
|
mode: :auto, # :auto | :ractor | :threaded
|
|
226
232
|
queue_depth: 1024, # bounded queue; overflow → 503
|
|
@@ -253,10 +259,27 @@ server.shutdown # graceful: drain → deadline → abort straggler
|
|
|
253
259
|
always counts as "shareable" (classes are), even if calling it touches
|
|
254
260
|
unshareable state. Force `:threaded` for those.
|
|
255
261
|
|
|
262
|
+
### Sharded I/O
|
|
263
|
+
|
|
264
|
+
`io_shards true` (off by default) moves HTTP I/O from Tokio's shared
|
|
265
|
+
multi-thread runtime onto current-thread shards: one thread accepts and
|
|
266
|
+
hands each connection to the least-loaded shard, which then owns it for
|
|
267
|
+
its lifetime—no work-stealing, no cross-thread wakeups on the hot path.
|
|
268
|
+
Fast handlers gain double-digit throughput; Ruby-bound endpoints are
|
|
269
|
+
unchanged. Orthogonal to `mode`: it reshapes the Rust side only.
|
|
270
|
+
|
|
271
|
+
```ruby
|
|
272
|
+
# kino.rb
|
|
273
|
+
io_shards true
|
|
274
|
+
io_threads 4 # optional; default: half the available CPUs
|
|
275
|
+
```
|
|
276
|
+
|
|
256
277
|
## Config file and CLI
|
|
257
278
|
|
|
258
|
-
Settings can live in a Puma-style Ruby DSL file.
|
|
259
|
-
|
|
279
|
+
Settings can live in a Puma-style Ruby DSL file: `kino.rb` in the
|
|
280
|
+
working directory, or `config/kino.rb` (the Rails layout), is picked up
|
|
281
|
+
automatically; `-C PATH` names any other. Precedence: explicit kwargs
|
|
282
|
+
and CLI flags > config file > defaults.
|
|
260
283
|
|
|
261
284
|
```ruby
|
|
262
285
|
# kino.rb
|
|
@@ -374,11 +397,11 @@ server.stats
|
|
|
374
397
|
# plus lane_depths: [...] when lane dispatch is on
|
|
375
398
|
```
|
|
376
399
|
|
|
377
|
-
From the outside, `kill -USR1 <pid>`
|
|
400
|
+
From the outside, `kill -USR1 <pid>` logs the same snapshot as one line
|
|
378
401
|
(pair it with `pidfile` to find the pid):
|
|
379
402
|
|
|
380
403
|
```
|
|
381
|
-
|
|
404
|
+
kino[4213] main: stats mode=:ractor lanes=false workers=8 threads=1 batch=1 respawns=0 queued=0 in_flight=2 served=1041 rejected=0 timeouts=0
|
|
382
405
|
```
|
|
383
406
|
|
|
384
407
|
For pull-based monitoring, `control_bind "127.0.0.1:9293"` (or a
|
|
@@ -422,16 +445,28 @@ box). There are two native pieces. Both write through a lock-free
|
|
|
422
445
|
channel to a Rust flusher thread, so request threads never take a log
|
|
423
446
|
mutex and never make a write syscall:
|
|
424
447
|
|
|
425
|
-
- **Access log** (`log_requests true`):
|
|
426
|
-
including the 503s that never reach your app.
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
448
|
+
- **Access log** (`log_requests true`): two records per request to
|
|
449
|
+
stdout, including the 503s that never reach your app. The arrival line
|
|
450
|
+
is queued before the app runs, so a request that hangs shows as an
|
|
451
|
+
arrow with no answer; the completion line carries the status, the
|
|
452
|
+
total, and a timing breakdown: `ruby` is the time the request spent in
|
|
453
|
+
Ruby (with the GC pause and the objects allocated during it), `kino`
|
|
454
|
+
the server's own overhead, `wait` the queue time before a worker took
|
|
455
|
+
it. Recommended in development; cheap enough for production. On color
|
|
456
|
+
terminals the completion line is tinted by status class: 2xx green,
|
|
457
|
+
3xx yellow, 4xx maroon, 5xx bright red:
|
|
430
458
|
|
|
431
459
|
```
|
|
432
|
-
|
|
460
|
+
2026-08-22 14:03:11 +0300 → GET /users?q=1 from 127.0.0.1
|
|
461
|
+
2026-08-22 14:03:11 +0300 ← 200 GET /users?q=1 12.4ms (ruby 9.1ms [gc 0.8ms; 1.5k obj]; kino 3.2ms; wait 0.1ms)
|
|
433
462
|
```
|
|
434
463
|
|
|
464
|
+
The GC and allocation figures come from the VM's process-wide
|
|
465
|
+
counters, so they appear only where one request at a time can own
|
|
466
|
+
them: in `:threaded` mode, or in `:ractor` mode with `workers 1`.
|
|
467
|
+
Parallel ractors would bill each other's work, so there the breakdown
|
|
468
|
+
is `(ruby; kino; wait)` alone.
|
|
469
|
+
|
|
435
470
|
- **`Kino::Logger`**: a `::Logger` over the same async sink, for your
|
|
436
471
|
app's own logging (`Kino::Logger.new("log/production.log")`, or no
|
|
437
472
|
argument for stdout). The raw IO-like device is `Kino::Logger::Device`,
|
|
@@ -473,6 +508,27 @@ lines/s), the sink drops lines instead of blocking request threads.
|
|
|
473
508
|
These trade-offs are measured in
|
|
474
509
|
[doc/benchmarks.md](doc/benchmarks.md#logging-costs).
|
|
475
510
|
|
|
511
|
+
**Server lines.** Everything Kino says about itself (draining, a crash
|
|
512
|
+
and its respawn, a hook that raised, quarantine, the stats line,
|
|
513
|
+
`rack.errors`) reads `kino[<pid>] <source>: message`, the source being
|
|
514
|
+
the worker that spoke, `worker-3` (or `worker-3/thread-2` in a
|
|
515
|
+
multi-threaded ractor), or `main`. On color terminals the label is dim
|
|
516
|
+
for notes, yellow for warnings, red for errors; the message stays plain.
|
|
517
|
+
A failed request gets a report instead of a bare backtrace: the request
|
|
518
|
+
line, the error, and where it raised in your code, then the trace with
|
|
519
|
+
your frames first, relative to the working directory, and the rest
|
|
520
|
+
folded:
|
|
521
|
+
|
|
522
|
+
```
|
|
523
|
+
kino[4213] worker-2: 500 GET /boom · RuntimeError: kaboom (app.rb:12:in 'explode')
|
|
524
|
+
app.rb:12:in 'explode'
|
|
525
|
+
/usr/lib/ruby/gems/4.0.0/gems/rack-3.2.7/lib/rack/builder.rb:...
|
|
526
|
+
… 38 more
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Hooks can log through the same channel with `Kino::Log.info`, `.warn`,
|
|
530
|
+
and `.error`; it is safe inside worker ractors.
|
|
531
|
+
|
|
476
532
|
## Timer waits
|
|
477
533
|
|
|
478
534
|
`Kino.sleep(seconds)` is a high-resolution sleep on the OS clock with
|
|
@@ -491,8 +547,9 @@ optional in Rack 3.
|
|
|
491
547
|
|
|
492
548
|
## Rails
|
|
493
549
|
|
|
494
|
-
Rails (edge) runs on Kino today in `:threaded` mode
|
|
495
|
-
`examples/rails-hello`. Ractor-mode Rails
|
|
550
|
+
Rails (edge) runs on Kino today in `:threaded` mode (`rails server -u
|
|
551
|
+
kino`, or the `kino` CLI); see `examples/rails-hello`. Ractor-mode Rails
|
|
552
|
+
is blocked upstream. The exact
|
|
496
553
|
blockers, the `Ruby::Box` findings, and what would unlock it are written
|
|
497
554
|
up in [doc/rails-on-ractors.md](doc/rails-on-ractors.md). The example
|
|
498
555
|
ships a probe script that re-tests against whatever Rails you bundle.
|
data/doc/architecture.md
CHANGED
|
@@ -11,10 +11,13 @@ tokio (Rust threads) Ruby
|
|
|
11
11
|
└──────────────────────────┘ └────────────────────────────┘
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
All network I/O lives in Rust on
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
All network I/O lives in Rust on tokio runtimes; hyper parses HTTP/1.1 and
|
|
15
|
+
handles keep-alive; rustls terminates TLS. The default is Tokio's
|
|
16
|
+
multi-thread runtime. With `io_shards true`, one current-thread runtime
|
|
17
|
+
accepts connections and assigns them to current-thread I/O shards, where
|
|
18
|
+
each connection stays for its lifetime. Ruby never touches a socket. Each
|
|
19
|
+
request becomes a Rust-side `RequestCtx` pushed to a bounded flume MPMC
|
|
20
|
+
queue; Ruby workers pull from it.
|
|
18
21
|
|
|
19
22
|
## Topology
|
|
20
23
|
|
data/exe/kino
CHANGED
|
@@ -9,10 +9,6 @@
|
|
|
9
9
|
# process-global bits an executable owns.
|
|
10
10
|
|
|
11
11
|
Warning[:experimental] = false
|
|
12
|
-
# Startup output must land immediately even when stdout is a pipe or file
|
|
13
|
-
# (process supervisors, `kino > server.log`); block buffering would hold
|
|
14
|
-
# the banner back until exit.
|
|
15
|
-
$stdout.sync = true
|
|
16
12
|
|
|
17
13
|
# Running from a git checkout (no installed gem, no bundler context):
|
|
18
14
|
# prefer the checkout's own lib so `require "kino"` resolves.
|
data/ext/kino/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "kino"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.5.0"
|
|
4
4
|
edition = "2021"
|
|
5
5
|
authors = ["Yaroslav Markin <yaroslav@markin.net>"]
|
|
6
6
|
license = "MIT"
|
|
@@ -26,7 +26,9 @@ hyper-util = { version = "0.1", features = ["server", "tokio", "http1"] }
|
|
|
26
26
|
http = "1"
|
|
27
27
|
http-body-util = "0.1"
|
|
28
28
|
bytes = "1"
|
|
29
|
-
|
|
29
|
+
# Local-time stamps for the access log: the system zone only (no bundled
|
|
30
|
+
# tzdb, no serde), which keeps the dependency tree to jiff itself.
|
|
31
|
+
jiff = { version = "0.2", default-features = false, features = ["std", "tz-system", "tzdb-zoneinfo"] }
|
|
30
32
|
# ring (not the default aws-lc-rs) as the rustls crypto provider: it
|
|
31
33
|
# cross-compiles cleanly in rb-sys-dock containers, while aws-lc-sys wants
|
|
32
34
|
# cmake and a per-target C toolchain.
|