zapp 0.2.1 → 0.2.2
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/.rubocop.yml +4 -1
- data/Gemfile.lock +1 -1
- data/examples/config/app.rb +5 -0
- data/examples/config/puma.rb +10 -0
- data/examples/config/zapp.rb +13 -0
- data/lib/zapp/configuration.rb +9 -1
- data/lib/zapp/http_context/request.rb +2 -2
- data/lib/zapp/logger/base.rb +103 -0
- data/lib/zapp/logger.rb +20 -57
- data/lib/zapp/server.rb +6 -4
- data/lib/zapp/socket_pipe/receiver.rb +0 -8
- data/lib/zapp/version.rb +1 -1
- data/lib/zapp/worker/request_processor.rb +27 -32
- data/lib/zapp/worker.rb +5 -6
- data/lib/zapp/worker_pool.rb +6 -5
- data/lib/zapp.rb +5 -2
- metadata +6 -96
- data/examples/rails-app/.browserslistrc +0 -1
- data/examples/rails-app/.gitattributes +0 -10
- data/examples/rails-app/.gitignore +0 -40
- data/examples/rails-app/.ruby-version +0 -1
- data/examples/rails-app/Gemfile +0 -58
- data/examples/rails-app/Gemfile.lock +0 -255
- data/examples/rails-app/Rakefile +0 -8
- data/examples/rails-app/app/assets/config/manifest.js +0 -2
- data/examples/rails-app/app/assets/images/.keep +0 -0
- data/examples/rails-app/app/assets/stylesheets/application.css +0 -15
- data/examples/rails-app/app/channels/application_cable/channel.rb +0 -6
- data/examples/rails-app/app/channels/application_cable/connection.rb +0 -6
- data/examples/rails-app/app/controllers/application_controller.rb +0 -4
- data/examples/rails-app/app/controllers/concerns/.keep +0 -0
- data/examples/rails-app/app/helpers/application_helper.rb +0 -4
- data/examples/rails-app/app/javascript/channels/consumer.js +0 -6
- data/examples/rails-app/app/javascript/channels/index.js +0 -5
- data/examples/rails-app/app/javascript/packs/application.js +0 -13
- data/examples/rails-app/app/jobs/application_job.rb +0 -9
- data/examples/rails-app/app/mailers/application_mailer.rb +0 -6
- data/examples/rails-app/app/models/application_record.rb +0 -5
- data/examples/rails-app/app/models/concerns/.keep +0 -0
- data/examples/rails-app/app/views/layouts/application.html.erb +0 -16
- data/examples/rails-app/app/views/layouts/mailer.html.erb +0 -13
- data/examples/rails-app/app/views/layouts/mailer.text.erb +0 -1
- data/examples/rails-app/babel.config.js +0 -82
- data/examples/rails-app/bin/bundle +0 -118
- data/examples/rails-app/bin/rails +0 -7
- data/examples/rails-app/bin/rake +0 -7
- data/examples/rails-app/bin/setup +0 -38
- data/examples/rails-app/bin/spring +0 -16
- data/examples/rails-app/bin/webpack +0 -21
- data/examples/rails-app/bin/webpack-dev-server +0 -21
- data/examples/rails-app/bin/yarn +0 -19
- data/examples/rails-app/bin/zapp +0 -1
- data/examples/rails-app/config/application.rb +0 -24
- data/examples/rails-app/config/boot.rb +0 -6
- data/examples/rails-app/config/cable.yml +0 -10
- data/examples/rails-app/config/credentials.yml.enc +0 -1
- data/examples/rails-app/config/database.yml +0 -25
- data/examples/rails-app/config/environment.rb +0 -7
- data/examples/rails-app/config/environments/development.rb +0 -78
- data/examples/rails-app/config/environments/production.rb +0 -122
- data/examples/rails-app/config/environments/test.rb +0 -62
- data/examples/rails-app/config/initializers/application_controller_renderer.rb +0 -9
- data/examples/rails-app/config/initializers/assets.rb +0 -16
- data/examples/rails-app/config/initializers/backtrace_silencers.rb +0 -10
- data/examples/rails-app/config/initializers/content_security_policy.rb +0 -31
- data/examples/rails-app/config/initializers/cookies_serializer.rb +0 -7
- data/examples/rails-app/config/initializers/filter_parameter_logging.rb +0 -8
- data/examples/rails-app/config/initializers/inflections.rb +0 -17
- data/examples/rails-app/config/initializers/mime_types.rb +0 -5
- data/examples/rails-app/config/initializers/permissions_policy.rb +0 -12
- data/examples/rails-app/config/initializers/wrap_parameters.rb +0 -16
- data/examples/rails-app/config/locales/en.yml +0 -33
- data/examples/rails-app/config/puma.rb +0 -45
- data/examples/rails-app/config/routes.rb +0 -5
- data/examples/rails-app/config/spring.rb +0 -8
- data/examples/rails-app/config/storage.yml +0 -34
- data/examples/rails-app/config/webpack/development.js +0 -5
- data/examples/rails-app/config/webpack/environment.js +0 -3
- data/examples/rails-app/config/webpack/production.js +0 -5
- data/examples/rails-app/config/webpack/test.js +0 -5
- data/examples/rails-app/config/webpacker.yml +0 -92
- data/examples/rails-app/config/zapp.rb +0 -10
- data/examples/rails-app/config.ru +0 -7
- data/examples/rails-app/db/seeds.rb +0 -8
- data/examples/rails-app/lib/assets/.keep +0 -0
- data/examples/rails-app/lib/tasks/.keep +0 -0
- data/examples/rails-app/log/.keep +0 -0
- data/examples/rails-app/package.json +0 -17
- data/examples/rails-app/postcss.config.js +0 -12
- data/examples/rails-app/public/404.html +0 -67
- data/examples/rails-app/public/422.html +0 -67
- data/examples/rails-app/public/500.html +0 -66
- data/examples/rails-app/public/apple-touch-icon-precomposed.png +0 -0
- data/examples/rails-app/public/apple-touch-icon.png +0 -0
- data/examples/rails-app/public/favicon.ico +0 -0
- data/examples/rails-app/public/robots.txt +0 -1
- data/examples/rails-app/storage/.keep +0 -0
- data/examples/rails-app/test/application_system_test_case.rb +0 -7
- data/examples/rails-app/test/channels/application_cable/connection_test.rb +0 -15
- data/examples/rails-app/test/controllers/.keep +0 -0
- data/examples/rails-app/test/fixtures/files/.keep +0 -0
- data/examples/rails-app/test/helpers/.keep +0 -0
- data/examples/rails-app/test/integration/.keep +0 -0
- data/examples/rails-app/test/mailers/.keep +0 -0
- data/examples/rails-app/test/models/.keep +0 -0
- data/examples/rails-app/test/system/.keep +0 -0
- data/examples/rails-app/test/test_helper.rb +0 -17
- data/examples/rails-app/tmp/.keep +0 -0
- data/examples/rails-app/tmp/pids/.keep +0 -0
- data/examples/rails-app/vendor/.keep +0 -0
- data/examples/rails-app/yarn.lock +0 -6973
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6b4a4b6bdf9ac52a419e899e41344faf5ff15fdde704f70e93fe71cee334e70e
|
|
4
|
+
data.tar.gz: 4bd738d7ca1919ceef29612c5bf9a07a26ad8bda944b9adfc52197435dbb1a0a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 26554e64959a88e35bf1b7f9657909da61de5a92c980550df415ae0b5564f899b25922e1fbb69d79a39a72506fe903654a007555c8e85ce3b437eb1cbbf8ccb3
|
|
7
|
+
data.tar.gz: c790f81f5fa7d1867ee6f33c3fff92f3df44ece8051e9dc3f9ed260724dd0236a3d3fd1814ba3dcf54f3743713782445a5001f54fcb275872943665055593b64
|
data/.rubocop.yml
CHANGED
|
@@ -5,13 +5,16 @@ AllCops:
|
|
|
5
5
|
Exclude:
|
|
6
6
|
- examples/**/*
|
|
7
7
|
|
|
8
|
+
Gemspec/RequireMFA:
|
|
9
|
+
Enabled: false
|
|
10
|
+
|
|
8
11
|
Style/DocumentationMethod:
|
|
9
12
|
Enabled: false
|
|
10
13
|
Style/MissingElse:
|
|
11
14
|
Enabled: false
|
|
12
15
|
Style/StringLiterals:
|
|
13
16
|
EnforcedStyle: double_quotes
|
|
14
|
-
Style/
|
|
17
|
+
Style/InlineComment:
|
|
15
18
|
Enabled: false
|
|
16
19
|
Style/Copyright:
|
|
17
20
|
Enabled: false
|
data/Gemfile.lock
CHANGED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
require_relative("app")
|
|
2
|
+
|
|
3
|
+
max_threads_count = ENV.fetch("RAILS_MAX_THREADS", 5)
|
|
4
|
+
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
|
|
5
|
+
threads(min_threads_count, max_threads_count)
|
|
6
|
+
worker_timeout(3600) if ENV.fetch("RAILS_ENV", "development") == "development"
|
|
7
|
+
port(ENV.fetch("PORT", 3000))
|
|
8
|
+
environment(ENV.fetch("RAILS_ENV", "development"))
|
|
9
|
+
workers ENV.fetch("WEB_CONCURRENCY") { 2 }
|
|
10
|
+
app(App)
|
data/lib/zapp/configuration.rb
CHANGED
|
@@ -12,6 +12,7 @@ module Zapp
|
|
|
12
12
|
:parallelism,
|
|
13
13
|
:threads_per_worker,
|
|
14
14
|
:logger_class,
|
|
15
|
+
:logger_out_io,
|
|
15
16
|
:log_requests,
|
|
16
17
|
:log_uncaught_errors,
|
|
17
18
|
:host,
|
|
@@ -33,6 +34,7 @@ module Zapp
|
|
|
33
34
|
|
|
34
35
|
# Default logging behavior
|
|
35
36
|
logger_class: Zapp::Logger,
|
|
37
|
+
logger_out_io: $stdout,
|
|
36
38
|
log_requests: true,
|
|
37
39
|
log_uncaught_errors: true,
|
|
38
40
|
|
|
@@ -72,7 +74,7 @@ module Zapp
|
|
|
72
74
|
@app = new unless new.nil?
|
|
73
75
|
|
|
74
76
|
@app ||= begin
|
|
75
|
-
raise(Zapp::
|
|
77
|
+
raise(Zapp::ZappError, "Missing rackup file '#{rackup_file}'") unless File.exist?(rackup_file)
|
|
76
78
|
|
|
77
79
|
rack_app, = rack_builder.parse_file(rackup_file)
|
|
78
80
|
|
|
@@ -98,6 +100,12 @@ module Zapp
|
|
|
98
100
|
@logger_class = new
|
|
99
101
|
end
|
|
100
102
|
|
|
103
|
+
def logger_out_io(new = nil)
|
|
104
|
+
return @logger_out_io if new.nil?
|
|
105
|
+
|
|
106
|
+
@logger_out_io = new
|
|
107
|
+
end
|
|
108
|
+
|
|
101
109
|
def log_requests(new = nil)
|
|
102
110
|
return @log_requests if new.nil?
|
|
103
111
|
|
|
@@ -9,7 +9,7 @@ module Zapp
|
|
|
9
9
|
attr_reader(:raw, :data, :body)
|
|
10
10
|
|
|
11
11
|
# Request parsing is done threaded, but not in separate Ractors.
|
|
12
|
-
# So we allocate an HTTP parser per thread and
|
|
12
|
+
# So we allocate an HTTP parser per thread and assign it to this hash key in Thread.current
|
|
13
13
|
PARSER_THREAD_HASH_KEY = "PUMA_PARSER_INSTANCE"
|
|
14
14
|
|
|
15
15
|
def initialize(socket:)
|
|
@@ -19,7 +19,7 @@ module Zapp
|
|
|
19
19
|
|
|
20
20
|
parser.execute(data, raw, 0)
|
|
21
21
|
|
|
22
|
-
@body = Zapp::InputStream.new(string:
|
|
22
|
+
@body = Zapp::InputStream.new(string: parser.body)
|
|
23
23
|
|
|
24
24
|
parser.reset
|
|
25
25
|
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Zapp
|
|
4
|
+
class Logger
|
|
5
|
+
# Base contains all the logging functionality and is included both as class and instance methods of Zap::Logger
|
|
6
|
+
# This allows logging without creating new instances,
|
|
7
|
+
# while allowing Ractors to create their own instances for thread safety
|
|
8
|
+
module Base
|
|
9
|
+
attr_writer(:level, :prefix)
|
|
10
|
+
|
|
11
|
+
LEVELS = { TRACE: 0, DEBUG: 1, INFO: 2, WARN: 3, ERROR: 4 }.freeze
|
|
12
|
+
|
|
13
|
+
FROZEN_ENV = ENV.map { |k, v| [k.freeze, v.freeze] }
|
|
14
|
+
.to_h.freeze
|
|
15
|
+
|
|
16
|
+
# The hash key in Ractor.current that stores the mutex for writing to output
|
|
17
|
+
OUT_IO_MUTEX_KEY = "ZAPP_LOGGER_OUT_IO_MUTEX"
|
|
18
|
+
|
|
19
|
+
def trace(msg) = log("TRACE", msg)
|
|
20
|
+
|
|
21
|
+
def debug(msg) = log("DEBUG", msg)
|
|
22
|
+
|
|
23
|
+
def info(msg) = log("INFO", msg)
|
|
24
|
+
|
|
25
|
+
def warn(msg) = log("WARN", msg)
|
|
26
|
+
|
|
27
|
+
def error(msg) = log("ERROR", msg)
|
|
28
|
+
|
|
29
|
+
def level
|
|
30
|
+
@level ||= begin
|
|
31
|
+
log_level = FROZEN_ENV["LOG_LEVEL"]
|
|
32
|
+
|
|
33
|
+
if log_level == "" || log_level.nil?
|
|
34
|
+
LEVELS[:DEBUG]
|
|
35
|
+
else
|
|
36
|
+
resolved_level = LEVELS[log_level.upcase.to_sym]
|
|
37
|
+
|
|
38
|
+
if resolved_level.nil?
|
|
39
|
+
raise(
|
|
40
|
+
Zapp::ZappError,
|
|
41
|
+
"Invalid log level '#{log_level.upcase}', must be one of [#{LEVELS.keys.join(', ')}]"
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
resolved_level
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def log(current_level, msg, **_tags)
|
|
51
|
+
return unless level <= LEVELS[current_level.to_sym]
|
|
52
|
+
|
|
53
|
+
write("--- #{prefix} [#{current_level}] #{msg}\n")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def flush
|
|
57
|
+
writing_thread_pool.wait_for_termination(0.1)
|
|
58
|
+
|
|
59
|
+
out_io_mutex.synchronize do
|
|
60
|
+
out.flush
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param new_out [IO]
|
|
65
|
+
def out=(new_out)
|
|
66
|
+
@out = new_out
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
protected
|
|
70
|
+
|
|
71
|
+
# @return [IO]
|
|
72
|
+
def out
|
|
73
|
+
@out ||= Zapp.config.logger_out_io
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# @return [String]
|
|
77
|
+
def prefix = @prefix ||= Ractor.current.name
|
|
78
|
+
|
|
79
|
+
def write(msg)
|
|
80
|
+
writing_thread_pool.post do
|
|
81
|
+
out_io_mutex.synchronize do
|
|
82
|
+
out.print(msg)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# We really just use this as a queue
|
|
88
|
+
# TODO: There's probably a smarter way of doing this with less overhead,
|
|
89
|
+
# TODO: or maybe we should just actually write logs multi-threaded
|
|
90
|
+
def writing_thread_pool
|
|
91
|
+
@writing_thread_pool ||= Concurrent::ThreadPoolExecutor.new(
|
|
92
|
+
min_threads: 1,
|
|
93
|
+
max_threads: 1,
|
|
94
|
+
max_queue: 100
|
|
95
|
+
)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def out_io_mutex
|
|
99
|
+
Ractor.current[OUT_IO_MUTEX_KEY] ||= Thread::Mutex.new
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
data/lib/zapp/logger.rb
CHANGED
|
@@ -1,74 +1,37 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative("logger/base")
|
|
4
|
+
|
|
3
5
|
module Zapp
|
|
4
|
-
# The default logger for
|
|
6
|
+
# The default logger for Zapp
|
|
5
7
|
class Logger
|
|
6
|
-
|
|
7
|
-
# This allows logging without creating new instances,
|
|
8
|
-
# while allowing Ractors to create their own instances for thread safety
|
|
9
|
-
module Base
|
|
10
|
-
attr_writer(:level, :prefix)
|
|
11
|
-
|
|
12
|
-
LEVELS = { TRACE: 0, DEBUG: 1, INFO: 2, WARN: 3, ERROR: 4 }.freeze
|
|
13
|
-
|
|
14
|
-
FROZEN_ENV = ENV.map { |k, v| [k.freeze, v.freeze] }
|
|
15
|
-
.to_h.freeze
|
|
16
|
-
|
|
17
|
-
def trace(msg)
|
|
18
|
-
log("TRACE", msg)
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def debug(msg)
|
|
22
|
-
log("DEBUG", msg)
|
|
23
|
-
end
|
|
8
|
+
include(Zapp::Logger::Base)
|
|
24
9
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
10
|
+
def initialize
|
|
11
|
+
yield(self) if block_given?
|
|
12
|
+
end
|
|
28
13
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
class << self
|
|
15
|
+
# The hash key in Ractor.current that stores the global Zapp::Logger instance
|
|
16
|
+
GLOBAL_INSTANCE_KEY = "ZAPP_LOGGER_INSTANCE"
|
|
32
17
|
|
|
33
|
-
def
|
|
34
|
-
|
|
18
|
+
def instance
|
|
19
|
+
Ractor.current[GLOBAL_INSTANCE_KEY] ||= new
|
|
35
20
|
end
|
|
36
21
|
|
|
37
|
-
|
|
38
|
-
@level ||= begin
|
|
39
|
-
log_level = FROZEN_ENV["LOG_LEVEL"]
|
|
40
|
-
|
|
41
|
-
if log_level == "" || log_level.nil?
|
|
42
|
-
LEVELS[:DEBUG]
|
|
43
|
-
else
|
|
44
|
-
resolved_level = LEVELS[log_level.upcase.to_sym]
|
|
45
|
-
|
|
46
|
-
if resolved_level.nil?
|
|
47
|
-
raise(
|
|
48
|
-
Zapp::ZappError,
|
|
49
|
-
"Invalid log level '#{log_level.upcase}', must be one of [#{LEVELS.keys.join(', ')}]"
|
|
50
|
-
)
|
|
51
|
-
end
|
|
22
|
+
private
|
|
52
23
|
|
|
53
|
-
|
|
54
|
-
|
|
24
|
+
def method_missing(symbol, *args)
|
|
25
|
+
if respond_to_missing?(symbol)
|
|
26
|
+
instance.public_send(symbol, *args)
|
|
27
|
+
else
|
|
28
|
+
super
|
|
55
29
|
end
|
|
56
30
|
end
|
|
57
31
|
|
|
58
|
-
def
|
|
59
|
-
|
|
32
|
+
def respond_to_missing?(symbol, include_private = false)
|
|
33
|
+
instance.respond_to?(symbol) || super(symbol, include_private)
|
|
60
34
|
end
|
|
61
35
|
end
|
|
62
|
-
include(Zapp::Logger::Base)
|
|
63
|
-
|
|
64
|
-
def initialize
|
|
65
|
-
yield(self) if block_given?
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
class << self
|
|
69
|
-
include(Zapp::Logger::Base)
|
|
70
|
-
end
|
|
71
36
|
end
|
|
72
37
|
end
|
|
73
|
-
|
|
74
|
-
Zapp::Logger.prefix = "Zapp"
|
data/lib/zapp/server.rb
CHANGED
|
@@ -11,7 +11,7 @@ module Zapp
|
|
|
11
11
|
|
|
12
12
|
@socket_pipe_receiver = Zapp::SocketPipe::Receiver.new(pipe: @socket_pipe)
|
|
13
13
|
|
|
14
|
-
@worker_pool = Zapp::WorkerPool.new(
|
|
14
|
+
@worker_pool = Zapp::WorkerPool.new(socket_pipe: @socket_pipe, context_pipe: @context_pipe)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def run
|
|
@@ -38,16 +38,17 @@ module Zapp
|
|
|
38
38
|
Zapp::Logger.info("Received signal #{err.class.name}") unless err.nil?
|
|
39
39
|
Zapp::Logger.info("Gracefully shutting down workers, allowing request processing to finish")
|
|
40
40
|
|
|
41
|
-
socket_pipe_receiver.drain
|
|
42
41
|
worker_pool.drain
|
|
43
42
|
|
|
44
43
|
Zapp::Logger.info("Done. See you next time!")
|
|
44
|
+
Zapp::Logger.flush
|
|
45
45
|
end
|
|
46
46
|
|
|
47
47
|
private
|
|
48
48
|
|
|
49
49
|
def log_start
|
|
50
|
-
Zapp::Logger.info(
|
|
50
|
+
Zapp::Logger.info(
|
|
51
|
+
"
|
|
51
52
|
⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡
|
|
52
53
|
⚡ ███████╗ █████╗ ██████╗ ██████╗ ⚡
|
|
53
54
|
⚡ ╚══███╔╝██╔══██╗██╔══██╗██╔══██╗ ⚡
|
|
@@ -56,7 +57,8 @@ module Zapp
|
|
|
56
57
|
⚡ ███████╗██║ ██║██║ ██║ ⚡
|
|
57
58
|
⚡ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ⚡
|
|
58
59
|
⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡⚡
|
|
59
|
-
"
|
|
60
|
+
"
|
|
61
|
+
)
|
|
60
62
|
Zapp::Logger.info("Zapp version: #{Zapp::VERSION}")
|
|
61
63
|
Zapp::Logger.info("Environment: #{Zapp.config.mode}")
|
|
62
64
|
Zapp::Logger.info("Serving: #{Zapp.config.env[Rack::RACK_URL_SCHEME]}://#{Zapp.config.host}:#{Zapp.config.port}")
|
data/lib/zapp/version.rb
CHANGED
|
@@ -4,11 +4,9 @@ module Zapp
|
|
|
4
4
|
class Worker < Ractor
|
|
5
5
|
# Processes HTTP requests
|
|
6
6
|
class RequestProcessor
|
|
7
|
-
attr_reader(:
|
|
7
|
+
attr_reader(:socket_pipe_sender, :context_pipe)
|
|
8
8
|
|
|
9
|
-
def initialize(context_pipe:, socket_pipe
|
|
10
|
-
@app = app
|
|
11
|
-
@config = config
|
|
9
|
+
def initialize(context_pipe:, socket_pipe:)
|
|
12
10
|
@socket_pipe_sender = Zapp::SocketPipe::Sender.new(pipe: socket_pipe)
|
|
13
11
|
@context_pipe = context_pipe
|
|
14
12
|
end
|
|
@@ -16,26 +14,24 @@ module Zapp
|
|
|
16
14
|
def loop
|
|
17
15
|
while (context = context_pipe.take)
|
|
18
16
|
if context == Zapp::WorkerPool::SIGNALS[:EXIT]
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
Zapp::Logger.trace("Received exit signal, shutting down")
|
|
18
|
+
shutdown
|
|
21
19
|
break
|
|
22
20
|
end
|
|
23
21
|
|
|
22
|
+
process = lambda {
|
|
23
|
+
process(context: context)
|
|
24
|
+
}
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
log_request_time(context: context, &process)
|
|
31
|
-
else
|
|
32
|
-
process.call
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# We send sockets that the client hasn't closed yet,
|
|
36
|
-
# back to the main ractor for HTTP request parsing again
|
|
37
|
-
socket_pipe_sender.push(context.socket) unless context.client_closed?
|
|
26
|
+
if Zapp.config.log_requests
|
|
27
|
+
log_request_time(context: context, &process)
|
|
28
|
+
else
|
|
29
|
+
process.call
|
|
30
|
+
end
|
|
38
31
|
|
|
32
|
+
# We send sockets that the client hasn't closed yet,
|
|
33
|
+
# back to the main ractor for HTTP request parsing again
|
|
34
|
+
socket_pipe_sender.push(context.socket) unless context.client_closed?
|
|
39
35
|
end
|
|
40
36
|
end
|
|
41
37
|
|
|
@@ -43,16 +39,16 @@ module Zapp
|
|
|
43
39
|
|
|
44
40
|
# Processes an HTTP request
|
|
45
41
|
def process(context:)
|
|
46
|
-
env = prepare_env(data: context.req.data, body: context.req.body, env: config.env.dup)
|
|
42
|
+
env = prepare_env(data: context.req.data, body: context.req.body, env: Zapp.config.env.dup)
|
|
47
43
|
|
|
48
|
-
status, headers, response_body_stream =
|
|
44
|
+
status, headers, response_body_stream = Zapp.config.app.call(env)
|
|
49
45
|
|
|
50
46
|
response_body = body_stream_to_string(response_body_stream)
|
|
51
47
|
|
|
52
48
|
context.res.write(data: response_body, status: status, headers: headers)
|
|
53
49
|
rescue StandardError => e
|
|
54
50
|
context.res.write(data: "An unexpected error occurred", status: 500, headers: {})
|
|
55
|
-
|
|
51
|
+
Zapp::Logger.error("#{e}\n\n#{e.backtrace&.join(",\n")}") if Zapp.config.log_uncaught_errors
|
|
56
52
|
end
|
|
57
53
|
|
|
58
54
|
# Merges HTTP data and body into the env to be passed to the rack app
|
|
@@ -79,7 +75,7 @@ module Zapp
|
|
|
79
75
|
path = context.req.data["PATH_INFO"]
|
|
80
76
|
status = context.res.status
|
|
81
77
|
|
|
82
|
-
|
|
78
|
+
Zapp::Logger.info(
|
|
83
79
|
"#{method} #{path} - Completed in #{request_time}ms with status #{status}"
|
|
84
80
|
)
|
|
85
81
|
end
|
|
@@ -96,19 +92,18 @@ module Zapp
|
|
|
96
92
|
response_body
|
|
97
93
|
end
|
|
98
94
|
|
|
95
|
+
def shutdown
|
|
96
|
+
Zapp::Logger.flush
|
|
97
|
+
thread_pool.shutdown
|
|
98
|
+
end
|
|
99
|
+
|
|
99
100
|
def thread_pool
|
|
100
101
|
@thread_pool ||= Concurrent::ThreadPoolExecutor.new(
|
|
101
|
-
min_threads: config.threads_per_worker,
|
|
102
|
-
max_threads: config.threads_per_worker,
|
|
103
|
-
max_queue: 1000
|
|
102
|
+
min_threads: Zapp.config.threads_per_worker,
|
|
103
|
+
max_threads: Zapp.config.threads_per_worker,
|
|
104
|
+
max_queue: 1000
|
|
104
105
|
)
|
|
105
106
|
end
|
|
106
|
-
|
|
107
|
-
def logger
|
|
108
|
-
@logger ||= config.logger_class.new do |l|
|
|
109
|
-
l.prefix = Ractor.current.name
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
107
|
end
|
|
113
108
|
end
|
|
114
109
|
end
|
data/lib/zapp/worker.rb
CHANGED
|
@@ -6,19 +6,18 @@ module Zapp
|
|
|
6
6
|
# One worker processing requests in parallel
|
|
7
7
|
class Worker < Ractor
|
|
8
8
|
class << self
|
|
9
|
-
def new(context_pipe:, socket_pipe:,
|
|
9
|
+
def new(context_pipe:, socket_pipe:, index:)
|
|
10
10
|
super(
|
|
11
11
|
context_pipe,
|
|
12
12
|
socket_pipe,
|
|
13
|
-
app,
|
|
14
13
|
Zapp.config.dup,
|
|
15
14
|
name: name(index)
|
|
16
|
-
) do |context_pipe, socket_pipe,
|
|
15
|
+
) do |context_pipe, socket_pipe, config|
|
|
16
|
+
Ractor.current[Zapp::RACTOR_CONFIG_KEY] = config
|
|
17
|
+
|
|
17
18
|
processor = Zapp::Worker::RequestProcessor.new(
|
|
18
19
|
socket_pipe: socket_pipe,
|
|
19
|
-
context_pipe: context_pipe
|
|
20
|
-
app: app,
|
|
21
|
-
config: config
|
|
20
|
+
context_pipe: context_pipe
|
|
22
21
|
)
|
|
23
22
|
|
|
24
23
|
processor.loop
|
data/lib/zapp/worker_pool.rb
CHANGED
|
@@ -9,14 +9,13 @@ module Zapp
|
|
|
9
9
|
EXIT: :exit
|
|
10
10
|
}.freeze
|
|
11
11
|
|
|
12
|
-
def initialize(
|
|
12
|
+
def initialize(context_pipe:, socket_pipe:)
|
|
13
13
|
@context_pipe = context_pipe
|
|
14
14
|
@workers = []
|
|
15
15
|
Zapp.config.parallelism.times do |i|
|
|
16
16
|
@workers << Worker.new(
|
|
17
17
|
context_pipe: context_pipe,
|
|
18
18
|
socket_pipe: socket_pipe,
|
|
19
|
-
app: app,
|
|
20
19
|
index: i
|
|
21
20
|
)
|
|
22
21
|
end
|
|
@@ -30,9 +29,11 @@ module Zapp
|
|
|
30
29
|
# Finishes processing of all requests and shuts down workers
|
|
31
30
|
def drain
|
|
32
31
|
Zapp.config.parallelism.times { process(context: SIGNALS[:EXIT]) }
|
|
33
|
-
workers.map
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
workers.map do |w|
|
|
33
|
+
w.terminate
|
|
34
|
+
rescue Ractor::ClosedError
|
|
35
|
+
# Ractor has already exited
|
|
36
|
+
end
|
|
36
37
|
end
|
|
37
38
|
end
|
|
38
39
|
end
|
data/lib/zapp.rb
CHANGED
|
@@ -11,11 +11,14 @@ require("rack")
|
|
|
11
11
|
module Zapp
|
|
12
12
|
class ZappError < StandardError; end
|
|
13
13
|
|
|
14
|
+
# The hash key in Ractor.current that stores the global Zapp::Configuration instance
|
|
15
|
+
RACTOR_CONFIG_KEY = "ZAPP_CONFIG"
|
|
16
|
+
|
|
14
17
|
class << self
|
|
15
18
|
def config(reset: false)
|
|
16
|
-
|
|
19
|
+
Ractor.current[RACTOR_CONFIG_KEY] = Zapp::Configuration.new if reset
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
Ractor.current[RACTOR_CONFIG_KEY] ||= Zapp::Configuration.new
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def configure
|