0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-11 00:59:57 +08:00
discourse/config/pitchfork.conf.rb
David Taylor 9527868295
DEV: Replace JS build system with Rolldown (#35963)
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>
2026-05-29 11:11:55 +01:00

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