Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion falcon.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
spec.add_dependency "async-container-supervisor", "~> 0.6"
spec.add_dependency "async-http", "~> 0.75"
spec.add_dependency "async-http-cache", "~> 0.4"
spec.add_dependency "async-service", "~> 0.10"
spec.add_dependency "async-service", ">= 0.15.1"
spec.add_dependency "bundler"
spec.add_dependency "localhost", "~> 1.1"
spec.add_dependency "openssl", "~> 3.0"
Expand Down
26 changes: 3 additions & 23 deletions lib/falcon/environment/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# Released under the MIT License.
# Copyright, 2020-2025, by Samuel Williams.

require "async/service/generic"
require "async/service/managed/environment"
require "async/http/endpoint"

require_relative "../service/server"
Expand All @@ -13,6 +13,8 @@ module Falcon
module Environment
# Provides an environment for hosting a web application that uses a Falcon server.
module Server
include Async::Service::Managed::Environment

# The service class to use for the proxy.
# @returns [Class]
def service_class
Expand All @@ -25,21 +27,6 @@ def authority
self.name
end

# Number of instances to start. By default (when nil), uses `Etc.nprocessors`.
# @returns [Integer | nil]
def count
nil
end

# Options to use when creating the container.
def container_options
{
restart: true,
count: self.count,
health_check_timeout: 30,
}.compact
end

# The host that this server will receive connections for.
def url
"http://[::]:9292"
Expand Down Expand Up @@ -80,13 +67,6 @@ def client_endpoint
::Async::HTTP::Endpoint.parse(url)
end

# Any scripts to preload before starting the server.
#
# @returns [Array(String)] The list of scripts to preload.
def preload
[]
end

# Make a server instance using the given endpoint. The endpoint may be a bound endpoint, so we take care to specify the protocol and scheme as per the original endpoint.
#
# @parameter endpoint [IO::Endpoint] The endpoint to bind to.
Expand Down
80 changes: 30 additions & 50 deletions lib/falcon/service/server.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,21 @@
# Copyright, 2019-2025, by Samuel Williams.
# Copyright, 2020, by Daniel Evans.

require "async/service/generic"
require "async/service/managed/service"
require "async/container/supervisor/supervised"
require "async/http/endpoint"

require_relative "../server"

module Falcon
module Service
class Server < Async::Service::Generic
class Server < Async::Service::Managed::Service
def initialize(...)
super

@bound_endpoint = nil
end

# Preload any resources specified by the environment.
def preload!
root = @evaluator.root

if scripts = @evaluator.preload
scripts = Array(scripts)

scripts.each do |path|
Console.logger.info(self) {"Preloading #{path}..."}
full_path = File.expand_path(path, root)
load(full_path)
end
end
end

# Prepare the bound endpoint for the server.
def start
@endpoint = @evaluator.endpoint
Expand All @@ -42,47 +27,42 @@ def start
@bound_endpoint = @endpoint.bound
end

preload!

Console.logger.info(self) {"Starting #{self.name} on #{@endpoint}"}

super
end

# Setup the container with the application instance.
# @parameter container [Async::Container::Generic]
def setup(container)
container_options = @evaluator.container_options
health_check_timeout = container_options[:health_check_timeout]
# Run the service logic.
#
# @parameter instance [Object] The container instance.
# @parameter evaluator [Environment::Evaluator] The environment evaluator.
# @returns [Falcon::Server] The server instance.
def run(instance, evaluator)
if evaluator.key?(:make_supervised_worker)
Console.warn(self, "Async::Container::Supervisor is replaced by Async::Services::Supervisor, please update your service definition.")

evaluator.make_supervised_worker(instance).run
end

server = evaluator.make_server(@bound_endpoint)

container.run(name: self.name, **container_options) do |instance|
evaluator = @environment.evaluator
Async do |task|
server.run

Async do |task|
if @environment.implements?(Async::Container::Supervisor::Supervised)
evaluator.make_supervised_worker(instance).run
end

server = evaluator.make_server(@bound_endpoint)

server.run

instance.ready!

if health_check_timeout
Async(transient: true) do
while true
# We only update this if the health check is enabled. Maybe we should always update it?
instance.name = "#{self.name} (#{server.statistics_string} L=#{Fiber.scheduler.load.round(3)})"
sleep(health_check_timeout / 2)
instance.ready!
end
end
end

task.children.each(&:wait)
end
task.children.each(&:wait)
end

server
end

# Format the process title with server statistics.
#
# @parameter evaluator [Environment::Evaluator] The environment evaluator.
# @parameter server [Falcon::Server] The server instance.
# @returns [String] The formatted process title.
private def format_title(evaluator, server)
load = Fiber.scheduler.load.round(3)
"#{evaluator.name} (#{server.statistics_string} L=#{load})"
end

# Close the bound endpoint.
Expand Down
Loading