rails activesupport notifications - wrong db runtime value

余生颓废 提交于 2019-12-04 01:44:33

Ok, it seems like a bug. Let see, what's going on:

First of all, we have AR railtie for controller action and its implementation for setting db_runtime by using cleanup_view_runtime hook

def cleanup_view_runtime
      if ActiveRecord::Base.connected?
       db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
       runtime = super
       db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
       self.db_runtime = db_rt_before_render + db_rt_after_render
       runtime - db_rt_after_render
     else
       super
     end
end

App calls controller action -> action doing some db queries and renders some stuff -> before and after rendering AR Logger saves runtime data. Good.

Let see how works respond_with

def respond_with(*resources, &block)
  raise "In order to use respond_with, first you need to declare the formats your " <<
        "controller responds to in the class level" if self.class.mimes_for_respond_to.empty?

  if collector = retrieve_collector_from_mimes(&block)
    options = resources.size == 1 ? {} : resources.extract_options!
    options[:default_response] = collector.response
    (options.delete(:responder) || self.class.responder).call(self, resources, options)
  end
end

def self.call(*args)
  new(*args).respond
end

def to_format
  if get? || !has_errors? || response_overridden?
    default_render
  else
    display_errors
  end
rescue ActionView::MissingTemplate => e
  api_behavior(e)
end

It seems like too much code here, but you should see the callstack for this issue: respond_with -> self.class.responder.respond -> self.class.responder.to_format -> default_render -> default_renderer raise ActionView::MissingTemplate(because we don't have any). At this moment we can see implementation for rendering :json and :xml(api_behaviour) through catching ActionView::MissingTemplate.

Now we know how respond_with works, but AR Logger does'nt know.. cleanup_view_runtime hook is called twice: for default_renderer(at that time template data was prepared and some db queries were called, but we catch ActionView::MissingTemplate in rendering proccess)

db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
runtime = super # <-- here
db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime

and for api_behavour(at that time all template data was ready for rendering and no db queries)

Some messy explanation, but I hope it will be helpful :)

The Rails instrumentation works by wrapping the request at the Rack metal level and recording events for the different metrics. In both cases, the entire block should be wrapped and tracked by the standard instrumentation.

The only difference I can see after digging into the source is calling save() vs. save!(). It looks like the underlying method event subscriptions in ActiveRecord may behave differently.

Try changing your respond_with() example to use save vs. save! and see if it records the db_runtime properly? If so, that's a Rails bug, and you can work around it by mimicking the save! features using save.

UPDATE:

It's not straightforward and needs some tweak around..

Not unless you write your own Railtie to hook into this like Active Record, but it's a bit more complex than copying what Active Record has...

But here's how it does it:

1) Log Subscriber

2) Railtie

3) Controller Runtime

I hope that you got the hint of how to start getting into it.

Cheers!

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!