rails activesupport notifications - wrong db runtime value

后端 未结 3 1837
执笔经年
执笔经年 2021-02-20 08:38

I\'m trying to log requests for my REST API application. I\'m using rails notifications for this , like here http://railscasts.com/episodes/249-notifications-in-rails-3

相关标签:
3条回答
  • 2021-02-20 08:42

    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 :)

    0 讨论(0)
  • 2021-02-20 09:02

    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!

    0 讨论(0)
  • 2021-02-20 09:03

    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.

    0 讨论(0)
提交回复
热议问题