mirror of
https://github.com/discourse/discourse.git
synced 2026-08-11 00:59:57 +08:00
This replaces the old ember-cli build with a modern Rolldown build. In local testing, this provides an 80% improvement in build times, while remaining 100% backwards compatible for themes and plugins. As part of this move, we have decided to stop using a proxy in front of Discourse for development. Development should now be done directly against the Rails server. `bin/ember-cli -u` has been replaced with `bin/dev`. This will launch Rails on `:3000`, and will run the rolldown build in the background. Log output from both processes will be shown with an appropriate prefix. You should visit `:3000` in your browser. `:4200` will no longer serve anything. To help with migration, `bin/ember-cli` is now a backwards-compatible shim. It will print help information, and will launch a lightweight server on `:4200` with instructions to move to `:3000`. If you prefer to launch Rails and the JS build as separate commands, you can still do that. Rails boot commands are unchanged, and the rolldown development builder can be run using `bin/dev --only ember`. https://meta.discourse.org/t/403908 --------- Co-authored-by: Jarek Radosz <jarek@cvx.dev> Co-authored-by: Chris Manson <chris@manson.ie>
185 lines
6.1 KiB
Ruby
Vendored
185 lines
6.1 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
discourse_path = File.expand_path(File.expand_path(File.dirname(__FILE__)) + "/../")
|
|
enable_logstash_logger = ENV["ENABLE_LOGSTASH_LOGGER"] == "1"
|
|
stderr_log_path = "#{discourse_path}/log/unicorn.stderr.log"
|
|
|
|
if enable_logstash_logger
|
|
require_relative "../lib/discourse_logstash_logger"
|
|
require_relative "../lib/pitchfork_logstash_patch"
|
|
FileUtils.touch(stderr_log_path) if !File.exist?(stderr_log_path)
|
|
logger DiscourseLogstashLogger.logger(
|
|
logdev: stderr_log_path,
|
|
type: :unicorn,
|
|
customize_event: lambda { |event| event["@timestamp"] = ::Time.now.utc },
|
|
)
|
|
else
|
|
console_logger = Logger.new(STDOUT)
|
|
if ENV["RAILS_ENV"] != "production"
|
|
require_relative "../lib/dev_log_formatter"
|
|
console_logger.formatter = DevLogFormatter.new(color: ENV["NO_COLOR"].nil?)
|
|
|
|
# Drop per-worker spawn logs. Summary below covers that.
|
|
console_logger.level = Logger::WARN
|
|
STDOUT.puts "Starting Pitchfork..."
|
|
end
|
|
logger console_logger
|
|
end
|
|
|
|
worker_processes (ENV["UNICORN_WORKERS"] || 3).to_i
|
|
|
|
# stree-ignore
|
|
listen ENV["UNICORN_LISTENER"] || "#{ENV["UNICORN_BIND_ALL"] ? "" : "127.0.0.1:"}#{(ENV["UNICORN_PORT"] || 3000).to_i}", reuseport: true
|
|
|
|
if ENV["RAILS_ENV"] == "production"
|
|
# nuke workers after 30 seconds instead of 20 seconds (the default)
|
|
timeout 30
|
|
else
|
|
# we want a longer timeout in dev cause first request can be really slow
|
|
timeout(ENV["UNICORN_TIMEOUT"] && ENV["UNICORN_TIMEOUT"].to_i || 60)
|
|
end
|
|
|
|
# On some really constrained environments, the mold process can take a long
|
|
# time to spawn workers. This is a safety valve to prevent the mold process
|
|
# from giving up too early.
|
|
spawn_timeout(Integer(ENV["APP_SERVER_SPAWN_TIMEOUT"], exception: false) || 60)
|
|
|
|
check_client_connection false
|
|
|
|
if ENV["RAILS_ENV"] != "production"
|
|
# Pitchfork defaults to setpgid true, which moves workers into their own process group.
|
|
# This prevents interactive debuggers (binding.pry, etc.) from reading STDIN.
|
|
setpgid false
|
|
end
|
|
|
|
before_fork do |server|
|
|
Discourse.redis.close
|
|
|
|
throttle_time = Float(ENV["APP_SERVER_FORK_THROTTLE"], exception: false) || 1
|
|
sleep(throttle_time) if !Rails.env.development?
|
|
end
|
|
|
|
after_mold_fork do |server, mold|
|
|
if mold.generation.zero?
|
|
Discourse.preload_rails!
|
|
|
|
supervisor = ENV["UNICORN_SUPERVISOR_PID"].to_i
|
|
|
|
if supervisor > 0
|
|
Thread.new do
|
|
while true
|
|
unless File.exist?("/proc/#{supervisor}")
|
|
server.logger.error "Kill self, supervisor is gone"
|
|
Process.kill "TERM", Process.pid
|
|
end
|
|
sleep 2
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
Discourse.redis.close
|
|
Discourse.before_fork
|
|
end
|
|
|
|
oob_gc_enabled = ENV["DISCOURSE_DISABLE_MAJOR_GC_DURING_REQUESTS"] && RUBY_VERSION >= "3.4"
|
|
|
|
after_worker_fork do |server, worker|
|
|
DiscourseEvent.trigger(:web_fork_started)
|
|
Discourse.apply_worker_db_variables_overrides
|
|
Discourse.after_fork
|
|
SignalTrapLogger.instance.after_fork
|
|
|
|
GC.config(rgengc_allow_full_mark: false) if oob_gc_enabled
|
|
end
|
|
|
|
if oob_gc_enabled
|
|
after_request_complete do |_server, _worker, _rack_env|
|
|
GC.start if GC.latest_gc_info(:need_major_by)
|
|
end
|
|
end
|
|
|
|
before_service_worker_ready do |server, service_worker|
|
|
sidekiqs = ENV["UNICORN_SIDEKIQS"].to_i
|
|
|
|
if sidekiqs > 0
|
|
server.logger.info "starting #{sidekiqs} supervised sidekiqs"
|
|
|
|
require "demon/sidekiq"
|
|
Demon::Sidekiq.after_fork { DiscourseEvent.trigger(:sidekiq_fork_started) }
|
|
Demon::Sidekiq.start(sidekiqs, logger: server.logger)
|
|
|
|
if Discourse.enable_sidekiq_logging?
|
|
# Trap USR1, so we can re-issue to sidekiq workers
|
|
# but chain the default pitchfork implementation as well
|
|
old_handler =
|
|
Signal.trap("USR1") do
|
|
old_handler.call
|
|
|
|
# We have seen Sidekiq processes getting stuck in production sporadically when log rotation happens.
|
|
# The cause is currently unknown but we suspect that it is related to the master process and
|
|
# Sidekiq demon processes reopening logs at the same time as we noticed that worker processes only
|
|
# reopen logs after the master process is done. To workaround the problem, we are adding an arbitrary
|
|
# delay of 1 second to Sidekiq's log reopening procedure. The 1 second delay should be
|
|
# more than enough for the master process to finish reopening logs.
|
|
Demon::Sidekiq.kill("USR2")
|
|
end
|
|
end
|
|
end
|
|
|
|
DiscoursePluginRegistry.demon_processes.each do |demon_class|
|
|
server.logger.info "starting #{demon_class.prefix} demon"
|
|
demon_class.start(1, logger: server.logger)
|
|
end
|
|
|
|
if Rails.env.development? && !ENV["CI"]
|
|
Demon::PluginJsWatcher.start(verbose: false, logger: server.logger)
|
|
end
|
|
|
|
if Rails.env.development?
|
|
EmberCli.watch!
|
|
|
|
workers = server.worker_processes
|
|
parts = ["#{workers} worker#{"s" if workers != 1}"]
|
|
parts << "#{sidekiqs} sidekiq#{"s" if sidekiqs != 1}" if sidekiqs > 0
|
|
parts.concat(DiscoursePluginRegistry.demon_processes.map(&:prefix))
|
|
parts << "plugin JS watcher" if !ENV["CI"]
|
|
STDOUT.puts "Pitchfork ready on http://localhost:#{ENV["UNICORN_PORT"] || 3000} (#{parts.join(", ")})"
|
|
end
|
|
|
|
Thread.new do
|
|
while true
|
|
begin
|
|
sleep 60
|
|
|
|
if sidekiqs > 0
|
|
Demon::Sidekiq.ensure_running
|
|
Demon::Sidekiq.heartbeat_check
|
|
Demon::Sidekiq.rss_memory_check
|
|
end
|
|
|
|
DiscoursePluginRegistry.demon_processes.each { |demon_class| demon_class.ensure_running }
|
|
rescue => e
|
|
Rails.logger.warn(
|
|
"Error in demon processes heartbeat check: #{e}\n#{e.backtrace.join("\n")}",
|
|
)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
after_worker_timeout do |server, worker, timeout_info|
|
|
message = <<~MSG
|
|
Pitchfork worker is about to timeout, dumping backtrace for main thread
|
|
#{timeout_info.thread.backtrace&.join("\n")}
|
|
MSG
|
|
|
|
Rails.logger.error(message)
|
|
end
|
|
|
|
if RUBY_PLATFORM.include?("darwin") && ENV["RAILS_ENV"] != "production"
|
|
# macOS doesn't support the default :SOCK_SEQPACKET
|
|
# So we override it to avoid the warning
|
|
Pitchfork.instance_variable_set(:@socket_type, :SOCK_STREAM)
|
|
require_relative "../lib/freedom_patches/pitchfork_sock_stream"
|
|
end
|