2011-12-07 6 views
3

C'est un problème déroutant qui est difficile à nommer, et encore moins à décrire. Je vais commencer par les faits essentiels et ensuite donner ce que les informations de base pourraient être pertinentes.Weirdness avec mongoengine ReferenceField

Tenir compte deux modèles de documents mongoengine:

class Bar(Document): 
    # ... 
    # field definitions 
    # ... 
    def bar_func(self): 
     pass # ...or some arbitrary code 


class Foo(Document): 
    bar = ReferenceField(Bar) 

Ce qui suit est incohérente produisant un AttributeError sur notre serveur de production:

# Assume foo_id references a valid Foo document in Mongo 
# and that its 'bar' reference is to a valid Bar document. 
foo = Foo.objects.with_id(foo_id) 
foo.bar.bar_func() # <-- AttributeError on 'bar_func' 

Si je place le code de débogage juste avant l'emplacement de l'erreur , l'évaluation type(foo.bar) en tant que chaîne produit <class 'bson.dbref.DBRef'>. Évidemment, un DBRef n'a pas d'attribut bar_func, mais pourquoi un DBRef est-il renvoyé au lieu d'une instance de Bar?

autre code de débogage montre que la condition suivante échoue dans la fonction ReferenceField.__get__ dans mongoengine/fields.py:

if isinstance(value, (pymongo.dbref.DBRef)): 
     value = _get_db().dereference(value) 

Mais (pymongo.dbref.DBRef) est en fait bson.dbref.DBRef, qui semble être le même que type(foo.bar)! Pourquoi isinstance échoue?

C'est là que les choses se vraiment bizarre:

id(type(foo.bar)) == id(bson.dbref.DBRef) # <-- Evaluates to False! 

En d'autres termes, type(foo.bar) est un autre bson.dbref.DBRef que celle obtenue en faisant référence bson.dbref.DBRef directement. En fait, l'inspection du __dict__ de ces deux types montre différents emplacements de mémoire pour leurs fonctions et leurs propriétés.

Note: Je vais appeler le type retourné par type(foo.bar)fooDBRef pour des raisons pratiques ci-dessous, pour le distinguer du type référencé par bson.dbref.DBRef.

Pour déboguer plus loin, je modifié le code DBRef ajouter une métaclasse qui inspecte les modules du système au moment du type DBRef est créé, et stocke une liste des ID de ces modules dans un attribut de classe supplémentaire de DBRef. Les résultats montrent que l'ensemble des modules existants lorsque fooDBRef est créé est entièrement distinct à partir de l'ensemble des modules existant lorsque le type bson.dbref.DBRef nu est créé. Tous les ID de module pour un sont différents de tous les ID de module pour l'autre.

Certains facteurs éventuellement pertinents:

  • Le serveur sur lequel cette erreur se produit Runs mod_wsgi sous Apache.
  • Le serveur exécute deux sites Django différents sous wsgi (appelez-les site_a et site_b).
  • Foo est défini dans site_a.foo_app.models et Bar est défini dans site_b.bar_app.models.
  • site_a paramètres.py a site_b.bar_app dans INSTALLED_APPS.
  • Les demandes qui produisent l'erreur sont traitées par site_a.
  • Il y avait site_b.* modules dans sys.modules lorsque fooDBRef a été créé, mais pas site_a.* modules. L'inverse est vrai pour bson.dbref.DBRef.
  • Après un httpd reload l'erreur disparaît parfois pendant un petit moment, et retourne parfois dans les 0-10 tentatives.

Quelqu'un peut-il me aider à comprendre ce qui est à l'origine fooDBRef être différent de bson.dbref.DBRef?

Répondre

4

Utilisez-vous le mode embarqué ou le mode démon de mod_wsgi? Si vous utilisez le mode démon de mod_wsgi, déléguez-vous chaque site à un groupe de processus démon différent, puis forcez l'application à s'exécuter dans l'interpréteur Python principal?

Il se peut que le module client mongodb Python ne fonctionne pas correctement dans les sous-interpréteurs Python, en particulier lorsque le module est utilisé simultanément dans un sous-interpréteur différent du même processus. Par conséquent, vous devrez peut-être exécuter chaque site dans un groupe de processus démon distinct à l'aide de WSGIDaemonProcess/WSGIProcessGroup, puis forcer l'utilisateur de l'interpréteur principal Python à utiliser WSGIApplicationGroup avec l'argument '% {GLOBAL}'. Notez que lorsque vous forcez l'utilisation de l'interpréteur principal pour les deux sites, vous ne pouvez plus utiliser le mode incorporé ou les faire tous deux fonctionner dans le même groupe de processus démon. Par conséquent, pourquoi devez-vous forcer chacun à s'exécuter dans un groupe de processus démon séparé.

+0

Nous utilisions le mode intégré. Je suis passé en mode démon, en ajoutant les directives WSGIDaemonProcess, WSGIProcessGroup et WSGIApplicationGroup aux entrées VirtualHost et la directive WSGISocketPrefix au niveau racine de la configuration. Et ça a l'air d'avoir marché! Au moins, l'erreur ne s'est pas encore produite, et normalement elle aurait eu lieu à ce stade. Je vais attendre un jour ou deux pour m'assurer qu'il est parti et ensuite accepter la réponse. Merci pour l'aide! – Alanyst

+0

Je suis satisfait que cela fonctionne maintenant. Merci encore. – Alanyst

Questions connexes