2010-10-20 5 views
5

J'ai ce que je pense doit être un problème de concurrence. J'utilise le passager, les rails 2.3.5, mongoid 1.9.2, et mongo ruby ​​driver 1.0.9. J'utilise jQuery pour demander des données extraites de MongoDB, puis affichées dans le navigateur. Tout fonctionne bien jusqu'à ce que je commence à faire deux demandes en même temps. Dans le modèle ce sont les méthodes qui sont exécutées par les demandes:Ruby Mongo ou problème de concurrence Mongoid

Class Visit 
    include Mongoid::Document 
    ... 
    def self.cancellations_and_visits_by_therapist_graph(clinic_id) 
    visits = collection.group("function(x){ return { resource_id : x.resource_id } }", 
      {:clinic_id => clinic_id, :visit_date => { "$gte" => Time.now - 6.months, "$lte" => Time.now}}, 
      {:visits => 0, :cancel_no_shows => 0}, 
      'function(obj, count) { 
       if (obj.visit_status == "NO SHOW" || obj.visits_status == "CANCELLED") { 
       count.cancel_no_shows += 1; 
       } else { 
       count.visits += 1; 
       } 

      }') 

    visits = visits.group_by {|g| g['resource_id']} 

    Resource.any_in(:mysql_id => visits.keys).order_by([:last_name, :asc]).order_by([:first_name, :asc]).inject({ 'visits' => [], 'cancel_no_shows' => [], 'xlabels' => []}) do |formatted_visits, resource| 
     formatted_visits['visits'] << visits[resource.mysql_id.to_f].first['visits'] 
     formatted_visits['cancel_no_shows'] << visits[resource.mysql_id.to_f].first['cancel_no_shows'] 
     formatted_visits['xlabels'] << resource.last_name + ", " + resource.first_name 
     formatted_visits 
    end 
    end 



    def self.total_visits_for_graph(practice_id) 
    visits = collection.group("function(x) { return { clinic_id : x.clinic_id } }", 
          {:practice_id => practice_id, :visit_status => 'COMPLETE', :visit_date => { "$gte" => Time.now - 6.months, "$lte" => Time.now}}, 
          {:visits => 0}, "function(obj, count) { count.visits += 1; }") 

    visits = visits.group_by {|g| g['clinic_id']} 
    Clinic.any_in(:mysql_id => visits.keys).order_by([:name, :asc]).inject({ 'data' => [], 'xlabels' => []}) do |formatted_visits, clinic| 
     formatted_visits['data'] << visits[clinic.mysql_id.to_f].first['visits'] 
     formatted_visits['xlabels'] << clinic.name 
     formatted_visits 
    end 
    end 
end 

La meilleure façon de décrire le problème est que les résultats de Mongo sont se transmis au mauvais objet. Je lookated un exemple:

Cela a été retourné quand j'ai appelé CLinic.any_in (il est le résultat d'un des groupes):

{"group"=>{"$keyf"=>"function(x){ return { resource_id : x.resource_id } }", "cond"=>{:clinic_id=>101, :visit_date=>{"$gte"=>Tue Apr 20 15:34:37 +0800 2010, "$lte"=>Wed Oct 20 15:34:37 +0800 2010}}, "ns"=>"visits", "initial"=>{:visits=>0, :cancel_no_shows=>0}, "$reduce"=>"function(obj, count) {\n    if (obj.visit_status == \"NO SHOW\" || obj.visits_status == \"CANCELLED\") {\n    count.cancel_no_shows += 1;\n    } else {\n    count.visits += 1;\n    }\n\n   }"}} 

Ce (un objet clinique) a été retourné par la collection .group appel:

{"_id"=>BSON::ObjectId('4cb7d72b3bc5457800ce2e6f'), "name"=>"Corona", "practice_id"=>39, "mysql_id"=>101} 

Comme tous les bons problèmes concurancy les résultats sont radôme, parfois, il fonctionne très bien et parfois il explose. Je suis nouveau sur mongo et mongo donc je ne sais pas si c'est un problème avec mongo ou mongo, mais je pense que c'est lié à Mongoid. J'inclus mon initialiseur que j'utilise pour charger Mongoid dans des rails. Toutes les idées ou même juste des idées de débogage supplémentaires sont grandement appréciés.

Connexion

mongoid_conf = YAML::load_file(Rails.root.join('config/mongoid.yml'))[Rails.env] 

Mongoid.configure do |config| 
    config.master = Mongo::Connection.new(mongoid_conf['host'], 27017, :pool_size => 5, :timeout => 5).db(mongoid_conf['database']) 
end 

Répondre

5

J'ai trouvé la solution à ce problème. Ce n'était en fait ni le pilote Mongo ni Mongoid, c'était passager. Lorsque Passenger s'étend sur les rails, il forke l'instance en cours afin que les descripteurs de fichiers (y compris les descripteurs TCP soient partagés entre les instances d'application). Cela signifie que ce mongo est écrit et lu par le même socket, ce qui est à l'origine du problème de simultanéité. La solution est de faire se reconnecter mongo chaque fois qu'il forks. C'est la solution que j'ai trouvé:

# Handle the creation of new processes by Phusion Passenger 
if defined?(PhusionPassenger) 
    PhusionPassenger.on_event(:starting_worker_process) do |forked| 
    if forked 
     # We're in smart spawning mode. 

     # Reset the connection to MongoDB 
     Mongoid.config.master.connection.close 
     load File.join(RAILS_ROOT, 'config/initializers/mongoid_init.rb') 
    else 
     # We're in conservative spawning mode. We don't need to do anything.  
    end 
    end 
end 

Référence http://groups.google.com/group/mongodb-user/browse_thread/thread/f31e2d23de38136a pour l'affichage de réponse originale et la discussion.

Questions connexes