zapp 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|