mirror of
https://github.com/discourse/discourse.git
synced 2025-05-22 07:53:49 +08:00
PERF: run post timings in background
This means that if a very large amount of registered users hit a single topic we will handle it gracefully, even if db gets slow.
This commit is contained in:
@ -1,5 +1,7 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_dependency 'method_profiler'
|
||||
|
||||
# This module allows us to hijack a request and send it to the client in the deferred job queue
|
||||
# For cases where we are making remote calls like onebox or proxying files and so on this helps
|
||||
# free up a unicorn worker while the remote IO is happening
|
||||
@ -13,20 +15,19 @@ module Hijack
|
||||
request.env['discourse.request_tracker.skip'] = true
|
||||
request_tracker = request.env['discourse.request_tracker']
|
||||
|
||||
# unicorn will re-cycle env, this ensures we keep the original copy
|
||||
env_copy = request.env.dup
|
||||
request_copy = ActionDispatch::Request.new(env_copy)
|
||||
# in the past unicorn would recycle env, this is not longer the case
|
||||
env = request.env
|
||||
request_copy = ActionDispatch::Request.new(env)
|
||||
|
||||
transfer_timings = MethodProfiler.transfer if defined? MethodProfiler
|
||||
transfer_timings = MethodProfiler.transfer
|
||||
|
||||
io = hijack.call
|
||||
|
||||
Scheduler::Defer.later("hijack #{params["controller"]} #{params["action"]}") do
|
||||
|
||||
MethodProfiler.start(transfer_timings) if defined? MethodProfiler
|
||||
|
||||
MethodProfiler.start(transfer_timings)
|
||||
begin
|
||||
Thread.current[Logster::Logger::LOGSTER_ENV] = env_copy
|
||||
Thread.current[Logster::Logger::LOGSTER_ENV] = env
|
||||
# do this first to confirm we have a working connection
|
||||
# before doing any work
|
||||
io.write "HTTP/1.1 "
|
||||
@ -43,7 +44,7 @@ module Hijack
|
||||
instance.instance_eval(&blk)
|
||||
rescue => e
|
||||
# TODO we need to reuse our exception handling in ApplicationController
|
||||
Discourse.warn_exception(e, message: "Failed to process hijacked response correctly", env: env_copy)
|
||||
Discourse.warn_exception(e, message: "Failed to process hijacked response correctly", env: env)
|
||||
end
|
||||
|
||||
unless instance.response_body || response.committed?
|
||||
@ -56,36 +57,40 @@ module Hijack
|
||||
|
||||
headers = response.headers
|
||||
# add cors if needed
|
||||
if cors_origins = env_copy[Discourse::Cors::ORIGINS_ENV]
|
||||
Discourse::Cors.apply_headers(cors_origins, env_copy, headers)
|
||||
if cors_origins = env[Discourse::Cors::ORIGINS_ENV]
|
||||
Discourse::Cors.apply_headers(cors_origins, env, headers)
|
||||
end
|
||||
|
||||
headers['Content-Length'] = body.bytesize
|
||||
headers['Content-Type'] = response.content_type || "text/plain"
|
||||
headers['Connection'] = "close"
|
||||
|
||||
status_string = Rack::Utils::HTTP_STATUS_CODES[instance.status.to_i] || "Unknown"
|
||||
io.write "#{instance.status} #{status_string}\r\n"
|
||||
status_string = Rack::Utils::HTTP_STATUS_CODES[response.status.to_i] || "Unknown"
|
||||
io.write "#{response.status} #{status_string}\r\n"
|
||||
|
||||
headers.each do |name, val|
|
||||
io.write "#{name}: #{val}\r\n"
|
||||
end
|
||||
|
||||
timings = MethodProfiler.stop
|
||||
if timings && duration = timings[:total_duration]
|
||||
io.write "X-Runtime: #{"%0.6f" % duration}\r\n"
|
||||
end
|
||||
|
||||
io.write "\r\n"
|
||||
io.write body
|
||||
rescue Errno::EPIPE, IOError
|
||||
# happens if client terminated before we responded, ignore
|
||||
io = nil
|
||||
ensure
|
||||
|
||||
MethodProfiler.clear
|
||||
Thread.current[Logster::Logger::LOGSTER_ENV] = nil
|
||||
|
||||
io.close if io rescue nil
|
||||
|
||||
if request_tracker
|
||||
status = instance.status rescue 500
|
||||
timings = MethodProfiler.stop if defined? MethodProfiler
|
||||
request_tracker.log_request_info(env_copy, [status, headers || {}, []], timings)
|
||||
request_tracker.log_request_info(env, [status, headers || {}, []], timings)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Reference in New Issue
Block a user