如何计算通过Mongoid访问/查询数据库的次数



我在Rails项目中使用Mongoid。为了提高大型查询的性能,我使用includes方法来急切地加载关系。

我想知道是否有一种简单的方法来计算代码块执行的查询的真实数量,这样我就可以检查我的includes是否真的像预期的那样减少了DB访问的数量。类似于:

# It will perform a large query to gather data from companies and their relationships
count = Mongoid.count_queries do
Company.to_csv
end
puts count # Number of DB access

我想使用此功能添加Rspec测试,以证明我的查询在更改后仍然有效(例如,从新关系添加数据时(。例如,在python的Django框架中,可以为此使用assertNumQueries方法。

我刚刚在一个使用mongo命令监控的小模块中实现了这个功能,以计算rspec套件中的mongo查询。

它可以这样使用:

expect { code }.to change { finds("users") }.by(3)
expect { code }.to change { updates("contents") }.by(1)
expect { code }.not_to change { inserts }

或者:

MongoSpy.flush
# ..code..
expect(MongoSpy.queries).to match(
"find" => { "users" => 1, "contents" => 1 },
"update" => { "users" => 1 }
)

以下是最新版本的Gist(可供复制(:https://gist.github.com/jarthod/ab712e8a31798799841c5677cea3d1a0

这是当前版本:

module MongoSpy
module Helpers
%w(find delete insert update).each do |op|
define_method(op.pluralize) { |ns = nil|
ns ? MongoSpy.queries[op][ns] : MongoSpy.queries[op].values.sum
}
end
end
class << self
def queries
@queries ||= Hash.new { |h, k| h[k] = Hash.new(0) }
end
def flush
@queries = nil
end
def started(event)
op = event.command.keys.first # find, update, delete, createIndexes, etc.
ns = event.command[op] # collection name
return unless ns.is_a?(String)
queries[op][ns] += 1
end
def succeeded(_); end
def failed(_); end
end
end
Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::COMMAND, MongoSpy)
RSpec.configure do |config|
config.include MongoSpy::Helpers
end

在rubygems.org上进行检查并没有产生任何效果。你最好看看像NewRelic、Scout或DataDog这样的应用程序性能工具。你也许可以通过获得一些现成的基准测试规范

https://github.com/piotrmurach/rspec-benchmark

您想要的是命令监视。使用Mongoid和Ruby驱动程序,您可以创建一个自定义的命令监视类,用于订阅服务器上的所有命令。

我从MongoRuby驱动程序的命令监控指南中改编了这篇文章。

对于这个特定的示例,请确保您的Rails应用程序设置了要调试的日志级别。您可以在这里阅读更多关于Rails记录器的信息。

您要做的第一件事是定义一个订阅者类。这是当Mongo::Client对数据库执行命令时告诉应用程序要做什么的类。以下是文档中的示例类:

class CommandLogSubscriber
include Mongo::Loggable
# called when a command is started
def started(event)
log_debug("#{prefix(event)} | STARTED | #{format_command(event.command)}")
end
# called when a command finishes successfully
def succeeded(event)
log_debug("#{prefix(event)} | SUCCEEDED | #{event.duration}s")
end
# called when a command terminates with a failure
def failed(event)
log_debug("#{prefix(event)} | FAILED | #{event.message} | #{event.duration}s")
end
private
def logger
Mongo::Logger.logger
end
def format_command(args)
begin
args.inspect
rescue Exception
'<Unable to inspect arguments>'
end
end
def format_message(message)
format("COMMAND | %s".freeze, message)
end
def prefix(event)
"#{event.address.to_s} | #{event.database_name}.#{event.command_name}"
end
end

(确保这个类自动加载到Rails应用程序中。(

接下来,您要将此订阅服务器连接到用于执行命令的客户端。

subscriber = CommandLogSubscriber.new
Mongo::Monitoring::Global.subscribe(Mongo::Monitoring::COMMAND, subscriber)
# This is the name of the default client, but it's possible you've defined
#   a client with a custom name in config/mongoid.yml
client = Mongoid::Clients.from_name('default')
client.subscribe( Mongo::Monitoring::COMMAND, subscriber)

现在,当Mongoid对数据库执行任何命令时,这些命令都会记录到您的控制台中。

# For example, if you have a model called Book
Book.create(title: "Narnia")
# => D, [2020-03-27T10:29:07.426209 #43656] DEBUG -- : COMMAND | localhost:27017 | mongoid_test_development.insert | STARTED | {"insert"=>"books", "ordered"=>true, "documents"=>[{"_id"=>BSON::ObjectId('5e7e0db3f8f498aa88b26e5d'), "title"=>"Narnia", "updated_at"=>2020-03-27 14:29:07.42239 UTC, "created_at"=>2020-03-27 14:29:07.42239 UTC}], "lsid"=>{"id"=><BSON::Binary:0x10600 type=uuid data=0xfff8a93b6c964acb...>}}
# => ...

您可以修改CommandLogSubscriber类以执行除日志记录之外的其他操作(例如递增全局计数器(。

最新更新