Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

rails activesupport notifications - wrong db runtime value

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

I can't understand how to solve one problem with rails notifications.

my initializer code

ActiveSupport::Notifications.subscribe "process_action.action_controller" do |name, start, finish, id, payload|
 p name
 p start 
 p finish
 p id
 p payload
end



Controller respond section

    class PostsController < ApplicationController
      # GET /posts
      # GET /posts.json

      respond_to  :json, :html
     ....
end

Controller create action

  def create
    @post = Post.new(params[:post])
    @post.save!
    respond_with(@post, :location => nil)
 end

Console output

"process_action.action_controller"
2013-02-02 20:13:11 +0200
2013-02-02 20:13:11 +0200
"951b8999e9b71d4a8949"
{:controller=>"PostsController", :action=>"create", :params=>{"utf8"=>"✓", "authenticity_token"=>"1WugY9gh6ZCRXjfBTuckye3c9XDvtCqMQ2JdBpCo88s=", "post"=>{"name"=>"post3", "title"=>"post3", "content"=>"post3"}, "commit"=>"Create Post", "action"=>"create", "controller"=>"posts"}, :format=>:html, :method=>"POST", :path=>"/posts", :status=>302, :view_runtime=>nil, :db_runtime=>0}

As you can see :db_runtime=>0

However if I change controller action code to default scaffold

  def create
    @post = Post.new(params[:post])
    #@post.save!
    #respond_with(@post)
    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render json: @post, status: :created, location: @post }
      else
        format.html { render action: "new" }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

I can see

"process_action.action_controller"
2013-02-02 20:22:51 +0200
2013-02-02 20:22:51 +0200
"bf2a3173c08a0fd9008e"
{:controller=>"PostsController", :action=>"create", :params=>{"utf8"=>"✓", "authenticity_token"=>"1WugY9gh6ZCRXjfBTuckye3c9XDvtCqMQ2JdBpCo88s=", "post"=>{"name"=>"post3", "title"=>"post3", "content"=>"post3"}, "commit"=>"Create Post", "action"=>"create", "controller"=>"posts"}, :format=>:html, :method=>"POST", :path=>"/posts", :status=>302, :view_runtime=>nil, :db_runtime=>4.727}

:db_runtime=>4.727

What is the reason of that and how I can fix it to make it work in first example ? Thanks !

UPD

 bundle show rails
/Users/admin/.rvm/gems/ruby-1.9.3-p125/gems/rails-3.2.11
rvm current
ruby-1.9.3-p125

UPD2

It seems like it doesn't work when I use respond_with! Can someone tell me why? Thanks

like image 482
Fivell Avatar asked Feb 02 '13 18:02

Fivell


1 Answers

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

like image 182
Dmitry Dedov Avatar answered Oct 11 '22 15:10

Dmitry Dedov