itsi-scheduler 0.2.11 → 0.2.12
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/Cargo.lock +1 -1
- data/ext/itsi_error/Cargo.toml +1 -1
- data/ext/itsi_rb_helpers/Cargo.toml +1 -1
- data/ext/itsi_scheduler/Cargo.toml +1 -1
- data/ext/itsi_server/Cargo.toml +1 -1
- data/ext/itsi_server/src/default_responses/mod.rs +3 -0
- data/ext/itsi_server/src/ruby_types/itsi_http_request.rs +15 -10
- data/ext/itsi_server/src/ruby_types/itsi_http_response.rs +2 -2
- data/ext/itsi_server/src/ruby_types/itsi_server/itsi_server_config.rs +6 -0
- data/ext/itsi_server/src/server/middleware_stack/middlewares/ruby_app.rs +13 -7
- data/ext/itsi_server/src/server/thread_worker.rs +4 -1
- data/lib/itsi/schedule_refinement.rb +96 -0
- data/lib/itsi/scheduler/version.rb +1 -1
- data/lib/itsi/scheduler.rb +1 -28
- metadata +2 -2
- data/sig/itsi_scheduler.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92a729e2814758244bb48ab697c3e6f2cc5d239414df2f5f106beb6356d46e02
|
4
|
+
data.tar.gz: 4104654365bb5ace7881d90ac9e49f849e899ee1ca2b17ae03a7f5bf9d16dac2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c8512c18fe4b5bde77b999ecf5f6772af2102d213bddfa8be2416178c2210b94787a8dc5531910cf85c815155ed2cf739999c08d6891342eb414a51bed5bc4f
|
7
|
+
data.tar.gz: 22914be4826e42af88929240d968ac493b8d9c82737807f33fe5aea0f979070eadac6de64104f912b0190dea2c3207e15199178f07c8c927ed5c5d0400936807
|
data/Cargo.lock
CHANGED
data/ext/itsi_error/Cargo.toml
CHANGED
data/ext/itsi_server/Cargo.toml
CHANGED
@@ -9,3 +9,6 @@ pub static NOT_FOUND_RESPONSE: LazyLock<ErrorResponse> = LazyLock::new(ErrorResp
|
|
9
9
|
|
10
10
|
pub static INTERNAL_SERVER_ERROR_RESPONSE: LazyLock<ErrorResponse> =
|
11
11
|
LazyLock::new(ErrorResponse::internal_server_error);
|
12
|
+
|
13
|
+
pub static SERVICE_UNAVAILABLE_RESPONSE: LazyLock<ErrorResponse> =
|
14
|
+
LazyLock::new(ErrorResponse::service_unavailable);
|
@@ -22,6 +22,7 @@ use super::{
|
|
22
22
|
itsi_http_response::ItsiHttpResponse,
|
23
23
|
};
|
24
24
|
use crate::{
|
25
|
+
default_responses::{INTERNAL_SERVER_ERROR_RESPONSE, SERVICE_UNAVAILABLE_RESPONSE},
|
25
26
|
server::{
|
26
27
|
byte_frame::ByteFrame,
|
27
28
|
http_message_types::{HttpRequest, HttpResponse},
|
@@ -186,16 +187,20 @@ impl ItsiHttpRequest {
|
|
186
187
|
} else {
|
187
188
|
&context.sender
|
188
189
|
};
|
189
|
-
match sender
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
190
|
+
match sender.try_send(RequestJob::ProcessHttpRequest(request, app)) {
|
191
|
+
Err(err) => match err {
|
192
|
+
async_channel::TrySendError::Full(_) => {
|
193
|
+
Ok(SERVICE_UNAVAILABLE_RESPONSE
|
194
|
+
.to_http_response(context.accept.clone())
|
195
|
+
.await)
|
196
|
+
}
|
197
|
+
async_channel::TrySendError::Closed(err) => {
|
198
|
+
error!("Error occurred: {:?}", err);
|
199
|
+
Ok(INTERNAL_SERVER_ERROR_RESPONSE
|
200
|
+
.to_http_response(context.accept.clone())
|
201
|
+
.await)
|
202
|
+
}
|
203
|
+
},
|
199
204
|
_ => match receiver.recv().await {
|
200
205
|
Some(first_frame) => Ok(response
|
201
206
|
.build(first_frame, receiver, shutdown_channel)
|
@@ -347,7 +347,7 @@ impl ItsiHttpResponse {
|
|
347
347
|
})?;
|
348
348
|
let header_value = unsafe { HeaderValue::from_maybe_shared_unchecked(value) };
|
349
349
|
if let Some(ref mut resp) = *self.data.response.write() {
|
350
|
-
resp.headers_mut().
|
350
|
+
resp.headers_mut().append(header_name, header_value);
|
351
351
|
}
|
352
352
|
Ok(())
|
353
353
|
}
|
@@ -364,7 +364,7 @@ impl ItsiHttpResponse {
|
|
364
364
|
})?;
|
365
365
|
for value in values {
|
366
366
|
let header_value = unsafe { HeaderValue::from_maybe_shared_unchecked(value) };
|
367
|
-
headers_mut.
|
367
|
+
headers_mut.append(&header_name, header_value);
|
368
368
|
}
|
369
369
|
}
|
370
370
|
}
|
@@ -69,6 +69,7 @@ pub struct ServerParams {
|
|
69
69
|
pub pin_worker_cores: bool,
|
70
70
|
pub scheduler_class: Option<String>,
|
71
71
|
pub oob_gc_responses_threshold: Option<u64>,
|
72
|
+
pub ruby_thread_request_backlog_size: Option<usize>,
|
72
73
|
pub middleware_loader: HeapValue<Proc>,
|
73
74
|
pub middleware: OnceLock<MiddlewareSet>,
|
74
75
|
pub binds: Vec<Bind>,
|
@@ -222,6 +223,10 @@ impl ServerParams {
|
|
222
223
|
let scheduler_class: Option<String> = rb_param_hash.fetch("scheduler_class")?;
|
223
224
|
let oob_gc_responses_threshold: Option<u64> =
|
224
225
|
rb_param_hash.fetch("oob_gc_responses_threshold")?;
|
226
|
+
|
227
|
+
let ruby_thread_request_backlog_size: Option<usize> =
|
228
|
+
rb_param_hash.fetch("ruby_thread_request_backlog_size")?;
|
229
|
+
|
225
230
|
let middleware_loader: Proc = rb_param_hash.fetch("middleware_loader")?;
|
226
231
|
let log_level: Option<String> = rb_param_hash.fetch("log_level")?;
|
227
232
|
let log_target: Option<String> = rb_param_hash.fetch("log_target")?;
|
@@ -310,6 +315,7 @@ impl ServerParams {
|
|
310
315
|
scheduler_threads,
|
311
316
|
streamable_body,
|
312
317
|
scheduler_class,
|
318
|
+
ruby_thread_request_backlog_size,
|
313
319
|
oob_gc_responses_threshold,
|
314
320
|
binds,
|
315
321
|
itsi_server_token_preference,
|
@@ -18,6 +18,7 @@ use std::sync::Arc;
|
|
18
18
|
pub struct RubyApp {
|
19
19
|
app: Arc<HeapValue<Proc>>,
|
20
20
|
request_type: RequestType,
|
21
|
+
script_name: Option<String>,
|
21
22
|
sendfile: bool,
|
22
23
|
nonblocking: bool,
|
23
24
|
base_path: Regex,
|
@@ -53,6 +54,9 @@ impl RubyApp {
|
|
53
54
|
let base_path_src = params
|
54
55
|
.funcall::<_, _, String>(Symbol::new("[]"), ("base_path",))
|
55
56
|
.unwrap_or("".to_owned());
|
57
|
+
let script_name = params
|
58
|
+
.funcall::<_, _, Option<String>>(Symbol::new("[]"), ("script_name",))
|
59
|
+
.unwrap_or(None);
|
56
60
|
let base_path = Regex::new(&base_path_src).unwrap();
|
57
61
|
|
58
62
|
let request_type: RequestType = params
|
@@ -65,6 +69,7 @@ impl RubyApp {
|
|
65
69
|
app: Arc::new(app.into()),
|
66
70
|
sendfile,
|
67
71
|
nonblocking,
|
72
|
+
script_name,
|
68
73
|
request_type,
|
69
74
|
base_path,
|
70
75
|
}))
|
@@ -82,13 +87,14 @@ impl MiddlewareLayer for RubyApp {
|
|
82
87
|
match self.request_type {
|
83
88
|
RequestType::Http => {
|
84
89
|
let uri = req.uri().path();
|
85
|
-
let script_name = self
|
86
|
-
.base_path
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
90
|
+
let script_name = self.script_name.clone().unwrap_or_else(|| {
|
91
|
+
self.base_path
|
92
|
+
.captures(uri)
|
93
|
+
.and_then(|caps| caps.name("base_path"))
|
94
|
+
.map(|m| m.as_str())
|
95
|
+
.unwrap_or("/")
|
96
|
+
.to_owned()
|
97
|
+
});
|
92
98
|
ItsiHttpRequest::process_request(
|
93
99
|
self.app.clone(),
|
94
100
|
req,
|
@@ -65,9 +65,12 @@ type ThreadWorkerBuildResult = Result<(
|
|
65
65
|
pub fn build_thread_workers(params: Arc<ServerParams>, pid: Pid) -> ThreadWorkerBuildResult {
|
66
66
|
let blocking_thread_count = params.threads;
|
67
67
|
let nonblocking_thread_count = params.scheduler_threads;
|
68
|
+
let ruby_thread_request_backlog_size: usize = params
|
69
|
+
.ruby_thread_request_backlog_size
|
70
|
+
.unwrap_or_else(|| (blocking_thread_count as u16 * 30) as usize);
|
68
71
|
|
69
72
|
let (blocking_sender, blocking_receiver) =
|
70
|
-
async_channel::bounded(
|
73
|
+
async_channel::bounded(ruby_thread_request_backlog_size);
|
71
74
|
let blocking_receiver_ref = Arc::new(blocking_receiver);
|
72
75
|
let blocking_sender_ref = blocking_sender;
|
73
76
|
let scheduler_class = load_scheduler_class(params.scheduler_class.clone())?;
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Itsi
|
2
|
+
module ScheduleRefinement
|
3
|
+
# Useful helper functions for using cooperative multi-tasking in Ruby.
|
4
|
+
# Opt-in to usage by executing `using Itsi::ScheduleRefinement` in any places
|
5
|
+
# you intend to use it.
|
6
|
+
#
|
7
|
+
# After this you can do things like the following
|
8
|
+
#
|
9
|
+
# 1. Launch batch concurrent fire-and-forget jobs.
|
10
|
+
# * 100.times.schedule_each{ sleep 0.1 }
|
11
|
+
#
|
12
|
+
# 2. Launch batch concurrent transofmrs
|
13
|
+
# See how `schedule_map` retains ordering, despite sleeping for randomized amount of time.
|
14
|
+
#
|
15
|
+
# * 100.times.schedule_map{|i| sleep Random.rand(0.0..0.05); i }
|
16
|
+
#
|
17
|
+
# 3. Manually organize fibers to run concurrently.
|
18
|
+
#
|
19
|
+
# require "net/http"
|
20
|
+
# schedule do
|
21
|
+
# req1, req2 = Queue.new, Queue.new
|
22
|
+
# schedule do
|
23
|
+
# puts "Making request 1"
|
24
|
+
# req1 << Net::HTTP.get(URI("http://httpbin.org/get"))
|
25
|
+
# puts "Finished request 1"
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# schedule do
|
29
|
+
# puts "Making request 2"
|
30
|
+
# req2 << Net::HTTP.get(URI("http://httpbin.org/get"))
|
31
|
+
# puts "Finished request 2"
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# res1, res2 = [req1, req2].map(&:pop)
|
35
|
+
# end
|
36
|
+
refine Kernel do
|
37
|
+
private def schedule(&blk) # rubocop:disable Metrics/MethodLength
|
38
|
+
return unless blk
|
39
|
+
|
40
|
+
if Fiber.scheduler.nil?
|
41
|
+
result = nil
|
42
|
+
Thread.new do
|
43
|
+
Fiber.set_scheduler Itsi::Scheduler.new
|
44
|
+
Fiber.schedule { result = blk.call }
|
45
|
+
end.join
|
46
|
+
result
|
47
|
+
else
|
48
|
+
Fiber.schedule(&blk)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
module EnumerableExtensions
|
54
|
+
using ScheduleRefinement
|
55
|
+
def schedule_each(&block)
|
56
|
+
enum = Enumerator.new do |y|
|
57
|
+
schedule do
|
58
|
+
each { |item| schedule{ y.yield(item) } }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
block_given? ? enum.each(&block) : enum.each
|
63
|
+
end
|
64
|
+
|
65
|
+
def schedule_map(&block)
|
66
|
+
return Enumerator.new do |y|
|
67
|
+
schedule do
|
68
|
+
with_index.each_with_object([]) do |(item, index), agg|
|
69
|
+
schedule do
|
70
|
+
agg[index] = (y << item)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end.map unless block_given?
|
75
|
+
schedule do
|
76
|
+
with_index.each_with_object([]) do |(item, index), agg|
|
77
|
+
schedule do
|
78
|
+
agg[index] = block[item]
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
refine Enumerator do
|
87
|
+
define_method(:schedule_each, EnumerableExtensions.instance_method(:schedule_each))
|
88
|
+
define_method(:schedule_map, EnumerableExtensions.instance_method(:schedule_map))
|
89
|
+
end
|
90
|
+
|
91
|
+
refine Enumerable do
|
92
|
+
define_method(:schedule_each, EnumerableExtensions.instance_method(:schedule_each))
|
93
|
+
define_method(:schedule_map, EnumerableExtensions.instance_method(:schedule_map))
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
data/lib/itsi/scheduler.rb
CHANGED
@@ -2,37 +2,10 @@
|
|
2
2
|
|
3
3
|
require_relative "scheduler/version"
|
4
4
|
require_relative "scheduler/itsi_scheduler"
|
5
|
+
require_relative "schedule_refinement"
|
5
6
|
|
6
7
|
module Itsi
|
7
|
-
module ScheduleUtils
|
8
|
-
def schedule(&block)
|
9
|
-
return to_enum(:schedule) unless block_given?
|
10
|
-
|
11
|
-
Object.schedule do
|
12
|
-
each { |item| Object.schedule { block.call(item) } }
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
8
|
class Scheduler
|
18
|
-
def self.enable_schedule_keyword!
|
19
|
-
Object.define_method(:schedule) do |&blk|
|
20
|
-
result = nil
|
21
|
-
return result unless blk
|
22
|
-
|
23
|
-
if Fiber.scheduler.nil?
|
24
|
-
Thread.new do
|
25
|
-
Fiber.set_scheduler Itsi::Scheduler.new
|
26
|
-
Fiber.schedule { result = blk[] }
|
27
|
-
end.join
|
28
|
-
else
|
29
|
-
Fiber.schedule { result = blk[] }
|
30
|
-
end
|
31
|
-
result
|
32
|
-
end
|
33
|
-
Enumerable.include(ScheduleUtils)
|
34
|
-
end
|
35
|
-
|
36
9
|
class Error < StandardError; end
|
37
10
|
|
38
11
|
def self.resume_token
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: itsi-scheduler
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wouter Coppieters
|
@@ -179,9 +179,9 @@ files:
|
|
179
179
|
- ext/itsi_tracing/target/debug/incremental/itsi_tracing-2kk4qj9gn5dg2/s-h5113124kv-0enwon2.lock
|
180
180
|
- ext/itsi_tracing/target/debug/incremental/itsi_tracing-2mwo0yas7dtw4/s-h510hfz1ha-1udgpei.lock
|
181
181
|
- itsi-scheduler-100.png
|
182
|
+
- lib/itsi/schedule_refinement.rb
|
182
183
|
- lib/itsi/scheduler.rb
|
183
184
|
- lib/itsi/scheduler/version.rb
|
184
|
-
- sig/itsi_scheduler.rbs
|
185
185
|
homepage: https://itsi.fyi
|
186
186
|
licenses:
|
187
187
|
- MIT
|
data/sig/itsi_scheduler.rbs
DELETED