Is find_or_create_by thread safe in Mongoid? - ruby-on-rails

I have a web app that uses Mongoid's find_or_create_by method.
poll = Poll.find_or_create_by(fields)
Before we go into production, I am trying to run through the failure scenarios, and it occurred to me that multiple users could try to access this resource using this method. Is there any likelihood that it could create multiple instances of the same object? What can I do to prevent that?

Disclaimer: I'm new to Mongoid and Rails so I could be totally wrong.
Looking at modifiable.rb and many.rb it doesn't appear to lock on any resource.
It looks to me like it simply does a "where().first" query and then if that does not return anything it performs a "create" query:
def find_or(method, attrs = {}, &block)
where(attrs).first || send(method, attrs, &block)
end
For find_or_create_by, "send" would call "create_document":
def create_document(method, attrs = nil, &block)
klass.__send__(method,
selector.reduce(attrs || {}) do |hash, (key, value)|
unless key.to_s =~ /\$/ || value.is_a?(Hash)
hash[key] = value
end
hash
end, &block)
end
Conclusion: To me this appears to be a convenience method, do not expect it to be "thread-safe". I wish there was more documentation available on this method.

sometimes it is not worth to implement a thread-safe code in MRI. because most of the time we have to go multiprocess deployments. so the easiest solution is database level uniqueness validations.
class Person
include Mongoid::Document
field :ssn
index({ ssn: 1 }, { unique: true })
end
so when save failed rollback the transaction. keep in mind that model level uniqueness validation is also error-prone.

Related

Interpolating an attribute's key before save

I'm using Rails 4 and have an Article model that has answer, side_effects, and benefits as attributes.
I am trying to create a before_save method that automatically looks at the side effects and benefits and creates links corresponding to another article on the site.
Instead of writing two virtually identical methods, one for side effects and one for benefits, I would like to use the same method and check to assure the attribute does not equal answer.
So far I have something like this:
before_save :link_to_article
private
def link_to_article
self.attributes.each do |key, value|
unless key == "answer"
linked_attrs = []
self.key.split(';').each do |i|
a = Article.where('lower(specific) = ?', i.downcase.strip).first
if a && a.approved?
linked_attrs.push("<a href='/questions/#{a.slug}' target=_blank>#{i.strip}</a>")
else
linked_attrs.push(i.strip)
end
end
self.key = linked_attrs.join('; ')
end
end
end
but chaining on the key like that gives me an undefined method 'key'.
How can I go about interpolating in the attribute?
in this bit: self.key you are asking for it to literally call a method called key, but what you want, is to call the method-name that is stored in the variable key.
you can use: self.send(key) instead, but it can be a little dangerous.
If somebody hacks up a new form on their browser to send you the attribute called delete! you don't want it accidentally called using send, so it might be better to use read_attribute and write_attribute.
Example below:
def link_to_article
self.attributes.each do |key, value|
unless key == "answer"
linked_attrs = []
self.read_attribute(key).split(';').each do |i|
a = Article.where('lower(specific) = ?', i.downcase.strip).first
if a && a.approved?
linked_attrs.push("<a href='/questions/#{a.slug}' target=_blank>#{i.strip}</a>")
else
linked_attrs.push(i.strip)
end
end
self.write_attribute(key, linked_attrs.join('; '))
end
end
end
I'd also recommend using strong attributes in the controller to make sure you're only permitting the allowed set of attributes.
OLD (before I knew this was to be used on all attributes)
That said... why do you go through every single attribute and only do something if the attribute is called answer? why not just not bother with going through the attributes and look directly at answer?
eg:
def link_to_article
linked_attrs = []
self.answer.split(';').each do |i|
a = Article.where('lower(specific) = ?', i.downcase.strip).first
if a && a.approved?
linked_attrs.push("<a href='/questions/#{a.slug}' target=_blank>#{i.strip}</a>")
else
linked_attrs.push(i.strip)
end
end
self.answer = linked_attrs.join('; ')
end

Interpolate Method Definition

This method does not have a description on the APIdock. I know instance_exec in Ruby is similar to the this binding mechanism in JavaScript.
def interpolate(sql, record = nil)
if sql.respond_to?(:to_proc)
owner.instance_exec(record, &sql)
else
sql
end
end
Could someone briefly describe it?
First of all, the check for respond_to?(:to_proc) is necessary to make sure sql might be converted to lambda (by ampersand & to be passed to instance_exec. To simplify things, one might treat sql here as being a lambda already:
def interpolate(sql, record = nil) # assume sql is lambda
owner.instance_exec(record, &sql)
end
As by documentation on instance_exec:
Executes the given block within the context of the receiver...
That said, lambda will be executed as it was the ordinal code, placed somewhere inside instance method of the receiver.
class Owner
def initialize
#records = [:zero, :one, :two]
end
end
record_by_index = ->(idx) { #records[idx] }
Owner.new.instance_exec 1, &record_by_index #⇒ :one
The code above is [more or less] an equivalent to:
class Owner
def initialize
#records = [:zero, :one, :two]
end
def record_by_index idx
#records[idx]
end
end
Owner.new.record_by_index(1) #⇒ :one
The actual parameters of call to instance_exec will be passed to the codeblock. In the context of Owner’s instance we have an access to instance variables, private methods, etc. Hope it helps.

How to query many fields, allowing for NULL

I have a Rails site that logs simple actions such as when people upvote and downvote information. For every new action, an EventLog is created.
What if the user changes his or her mind? I have an after_create callback that looks for complementary actions and deletes both if it finds a recent pair. For clarity, I mean that if a person upvotes something and soon cancels, both event_logs are deleted. What follows is my callback.
# Find duplicate events by searching nearly all the fields in the EventLog table
#duplicates = EventLog.where("user_id = ? AND event = ? AND project_id = ? AND ..., ).order("created_at DESC")
if #duplicates.size > 1
#duplicates.limit(2).destroy_all
end
The above code doesn't quite work because if any of the fields happen to be nil, the query returns [].
How can I write this code so it can handle null values, and/or is there a better way of doing this altogether?
If I understood this correctly,
some of the fields can be nil, and you want to find activity logs that have same user_id, same project_id or project id can be nil.
So I guess this query should work for you.
ActivityLog.where(user_id: <some_id> AND activity: <complementary_id> AND :project_id.in => [<some_project_id>, nil] ....)
This way you would get the complementary event logs where user_id is same and project id may or may not be present
class ActivityLog
QUERY_HASH = Proc.new{ {user_id: self.user_id,
activity: complementary_id(self.id),
and so on....
} }
How about:
# event_log.rb
def duplicate_attr_map
{
:user_id,
:project_id
}
end
def duplicates
attribs = duplicate_attr_map.reject_if(&:blank?)
query = attribs.map { |attr| "#{attr} = ?" }.join(' AND ')
values = attribs.map { |attr| self.send(attr) }
EventLog.where(query, *values).order("created_at DESC")
end
def delete_duplicates(n)
duplicates.limit(n).delete_all if duplicates.size > 1
end
# usage:
# EventLog.find(1).delete_duplicates(2)
not tested, could be improved

How to test the number of database calls in Rails

I am creating a REST API in rails. I'm using RSpec. I'd like to minimize the number of database calls, so I would like to add an automatic test that verifies the number of database calls being executed as part of a certain action.
Is there a simple way to add that to my test?
What I'm looking for is some way to monitor/record the calls that are being made to the database as a result of a single API call.
If this can't be done with RSpec but can be done with some other testing tool, that's also great.
The easiest thing in Rails 3 is probably to hook into the notifications api.
This subscriber
class SqlCounter< ActiveSupport::LogSubscriber
def self.count= value
Thread.current['query_count'] = value
end
def self.count
Thread.current['query_count'] || 0
end
def self.reset_count
result, self.count = self.count, 0
result
end
def sql(event)
self.class.count += 1
puts "logged #{event.payload[:sql]}"
end
end
SqlCounter.attach_to :active_record
will print every executed sql statement to the console and count them. You could then write specs such as
expect do
# do stuff
end.to change(SqlCounter, :count).by(2)
You'll probably want to filter out some statements, such as ones starting/committing transactions or the ones active record emits to determine the structures of tables.
You may be interested in using explain. But that won't be automatic. You will need to analyse each action manually. But maybe that is a good thing, since the important thing is not the number of db calls, but their nature. For example: Are they using indexes?
Check this:
http://weblog.rubyonrails.org/2011/12/6/what-s-new-in-edge-rails-explain/
Use the db-query-matchers gem.
expect { subject.make_one_query }.to make_database_queries(count: 1)
Fredrick's answer worked great for me, but in my case, I also wanted to know the number of calls for each ActiveRecord class individually. I made some modifications and ended up with this in case it's useful for others.
class SqlCounter< ActiveSupport::LogSubscriber
# Returns the number of database "Loads" for a given ActiveRecord class.
def self.count(clazz)
name = clazz.name + ' Load'
Thread.current['log'] ||= {}
Thread.current['log'][name] || 0
end
# Returns a list of ActiveRecord classes that were counted.
def self.counted_classes
log = Thread.current['log']
loads = log.keys.select {|key| key =~ /Load$/ }
loads.map { |key| Object.const_get(key.split.first) }
end
def self.reset_count
Thread.current['log'] = {}
end
def sql(event)
name = event.payload[:name]
Thread.current['log'] ||= {}
Thread.current['log'][name] ||= 0
Thread.current['log'][name] += 1
end
end
SqlCounter.attach_to :active_record
expect do
# do stuff
end.to change(SqlCounter, :count).by(2)

Clarifying a custom Rails 3.0 Validation with methods

I've created a custom validator in Rails 3.0 which validates whether a combination of columns is unique within a table. The entire code of the validation is:
class UniqueInProjectValidator < ActiveModel::EachValidator
def validate_each(object, attribute, value)
unless object.class.where("project_id = ? AND #{attribute} = ?", object.project_id, value).empty?
if object.new_record?
object.errors[attribute] << (options[:message] || "must be unique in each project")
else
orig_rec = object.class.find(object.id)
if value != orig_rec.method(attribute).call || object.project_id != orig_rec.project_id
object.errors[attribute] << (options[:message] || "must be unique in each project")
end
end
end
end
Note that it is not easy to recognize what the if statements do, so I was hoping to be able to replace the unless conditional with a def attribute_and_project_exist? method and the second if statement with a def attribute_or_project_changed? method. However when creating those methods, the arguments from validates_each do not pass because of encapsulation.
Now the question: Is there a way to somehow cleanly allow those variables to be accessed by my two newly created methods as one can do with column names in a model, or am I stuck with the options of either passing each argument again or leaving the hard to read conditional statements?
Thanks in advance!
I suppose you could clean it up a bit with one variable, one lambda, and one "return as soon as possible":
def validate_each(object, attribute, value)
# If there is no duplication then bail out right away as
# there is nothing to check. This reduces your nesting by
# one level. Using a variable here helps to make your
# intention clear.
attribute_and_project_exists = object.class.where("project_id = ? AND #{attribute} = ?", object.project_id, value).empty?
return unless attribute_and_project_exists
# This lambda wraps up your second chunk of ugly if-ness and saves
# you from computing the result unless you have to.
attribute_or_project_changed = lambda do
orig_rec = object.class.find(object.id)
value != orig_rec.method(attribute).call || object.project_id != orig_rec.project_id
end
# Note that || short-circuits so the lambda will only be
# called if you have an existing record.
if object.new_record? || attribute_or_project_changed.call
object.errors[attribute] << (options[:message] || "must be unique in each project")
end
end
I don't know how much better that is than your original but the logic and control flow is a lot clearer to me due to the nicer chunking.

Resources