Projet

Général

Profil

Bug #9891

possible mauvaise création de django_content_types à la création d'un tenant

Ajouté par Thomas Noël il y a environ 8 ans. Mis à jour il y a presque 8 ans.

Statut:
Fermé
Priorité:
Haut
Assigné à:
-
Catégorie:
-
Version cible:
-
Début:
06 février 2016
Echéance:
% réalisé:

0%

Temps estimé:
Patch proposed:
Non
Planning:

Description

combo:~# /etc/init.d/combo restart
[....] Restarting Portal Management System: combo[info] Applying migrations (migrate_schemas)...
=== Running migrate for schema public
Operations to perform:
  Synchronize unmigrated apps: multitenant, ckeditor, cmsplugin_blurp
  Apply all migrations: wcs, mellon, dataviz, family, sessions, admin, auth, contenttypes, momo, common, data, lingo
Synchronizing apps without migrations:
  Creating tables...
  Installing custom SQL...
  Installing indexes...
Running migrations:
  No migrations to apply.
=== Running migrate for schema portail_agent_alfortville_fr
Operations to perform:
  Synchronize unmigrated apps: multitenant, ckeditor, cmsplugin_blurp
  Apply all migrations: wcs, mellon, dataviz, family, sessions, admin, auth, contenttypes, momo, common, data, lingo
Synchronizing apps without migrations:
  Creating tables...
  Installing custom SQL...
  Installing indexes...
Running migrations:
  No migrations to apply.
Traceback (most recent call last):
  File "/usr/lib/combo/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/lib/python2.7/dist-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/usr/lib/python2.7/dist-packages/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/lib/python2.7/dist-packages/hobo/multitenant/management/commands/migrate_schemas.py", line 25, in run_from_argv
    super(MigrateSchemasCommand, self).run_from_argv(argv)
  File "/usr/lib/python2.7/dist-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/usr/lib/python2.7/dist-packages/django/core/management/base.py", line 338, in execute
    output = self.handle(*args, **options)
  File "/usr/lib/python2.7/dist-packages/hobo/multitenant/management/commands/migrate_schemas.py", line 42, in handle
    self.run_migrations(tenant.schema_name, settings.TENANT_APPS)
  File "/usr/lib/python2.7/dist-packages/hobo/multitenant/management/commands/migrate_schemas.py", line 58, in run_migrations
    command.execute(*self.args, **defaults)
  File "/usr/lib/python2.7/dist-packages/django/core/management/base.py", line 338, in execute
    output = self.handle(*args, **options)
  File "/usr/lib/python2.7/dist-packages/django/core/management/commands/migrate.py", line 165, in handle
    emit_post_migrate_signal(created_models, self.verbosity, self.interactive, connection.alias)
  File "/usr/lib/python2.7/dist-packages/django/core/management/sql.py", line 268, in emit_post_migrate_signal
    using=db)
  File "/usr/lib/python2.7/dist-packages/django/dispatch/dispatcher.py", line 198, in send
    response = receiver(signal=self, sender=sender, **named)
  File "/usr/lib/python2.7/dist-packages/django/contrib/auth/management/__init__.py", line 114, in create_permissions
    Permission.objects.using(using).bulk_create(perms)
  File "/usr/lib/python2.7/dist-packages/django/db/models/query.py", line 409, in bulk_create
    self._batched_insert(objs_without_pk, fields, batch_size)
  File "/usr/lib/python2.7/dist-packages/django/db/transaction.py", line 339, in __exit__
    connection.commit()
  File "/usr/lib/python2.7/dist-packages/django/db/backends/__init__.py", line 176, in commit
    self._commit()
  File "/usr/lib/python2.7/dist-packages/django/db/backends/__init__.py", line 145, in _commit
    return self.connection.commit()
  File "/usr/lib/python2.7/dist-packages/django/db/utils.py", line 94, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/usr/lib/python2.7/dist-packages/django/db/backends/__init__.py", line 145, in _commit
    return self.connection.commit()
django.db.utils.IntegrityError: ERREUR:  une instruction insert ou update sur la table « auth_permission » viole la contrainte de clé
étrangère « auth_content_type_id_508cf46651277a81_fk_django_content_type_id »
DETAIL:  La clé (content_type_id)=(16) n'est pas présente dans la table « django_content_type ».

[info] done.
[info] Collect static files (collectstatic)...

0 static files copied to '/var/lib/combo/collectstatic', 1044 unmodified.
[info] done.
. ok 

Fichiers


Demandes liées

Lié à Publik - Bug #10308: erreur à l'exécution de commandes de manage avec le dernier django-tenant-schemasFermé16 mars 2016

Actions

Historique

#1

Mis à jour par Frédéric Péters il y a environ 8 ans

Lors du déploiement.

Traceback (most recent call last):
  File "/usr/lib/combo/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/usr/lib/python2.7/dist-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/usr/lib/python2.7/dist-packages/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/usr/lib/python2.7/dist-packages/django/core/management/base.py", line 295, in run_from_argv
    stderr.write('%s: %s' % (e.__class__.__name__, e))
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 79: ordinal not in range(128)
[2016-02-06 10:23:27,338: INFO/MainProcess] Task hobo-deploy[33a36fcc-b1ab-49bd-aab9-acf88e24d7a6] succeeded in 25.2780017816s: None

En changeant pour mettre des %r :

'CommandError': CommandError("tenant creation failed (ERREUR:  une instruction insert ou update sur la table \xc2\xab auth_permission \xc2\xbb viole la contrainte de cl\xc3\xa9\n\xc3\xa9trang\xc3\xa8re \xc2\xab auth_content_type_id_508cf46651277a81_fk_django_content_type_id \xc2\xbb\nDETAIL:  La cl\xc3\xa9 (content_type_id)=(16) n'est pas pr\xc3\xa9sente dans la table \xc2\xab django_content_type \xc2\xbb.\n)",)

Et le schéma est créé donc lors de l'hobo_deploy suivant ça semble passer et en fait, non, c'est tout cassé.

#2

Mis à jour par Frédéric Péters il y a environ 8 ans

À exécuter le create_tenant en mode verbose :

...
  Applying wcs.0009_remove_wcscategorycell_link_page... OK
  Applying wcs.0010_auto_20151029_1535... OK
  Applying wcs.0011_auto_20151215_1121... OK
'CommandError': CommandError("tenant creation failed (ERREUR:  une instruction insert ou update sur la table \xc2\xab auth_permission \xc2\xbb viole la contrainte de cl\xc3\xa9\n\xc3\xa9trang\xc3\xa8re \xc2\xab auth_content_type_id_508cf46651277a81_fk_django_content_type_id \xc2\xbb\nDETAIL:  La cl\xc3\xa9 (content_type_id)=(16) n'est pas pr\xc3\xa9sente dans la table \xc2\xab django_content_type \xc2\xbb.\n)",)
#3

Mis à jour par Frédéric Péters il y a environ 8 ans

  • Projet changé de Admin système à Hobo
  • Sujet changé de crash migration combo prod à possible mauvaise création de django_content_types à la création d'un tenant

C'est passé avec :

+ from django.contrib.contenttypes.models import ContentType
...
            try:
                connection.set_schema_to_public()
                schema = TenantMiddleware.hostname2schema(hostname)
+                ContentType.objects.clear_cache()
                tenant = get_tenant_model()(schema_name=schema, domain_url=hostname)
                if verbosity >= 1:
                    print
                    print self.style.NOTICE("=== Creating schema ") \
                        + self.style.SQL_TABLE(tenant.schema_name)
                tenant.create_schema(check_if_exists=True)

Je dois partir, j'ai déplacé ça vers Hobo, si ça dit à quelqu'un de regarder pour faire un vrai patch.

#4

Mis à jour par Benjamin Dauvergne il y a environ 8 ans

Ça ressemble à un souci lié au cache des objets ContentType; le code multitenant contient des appels à ContentType.objects.clear_cache() quand on change de schéma mais là il doit en manquer un au bon endroit, je ne trouve pas la plateforme ou reproduire le bug, mais ajouter un print dans clear_cache et voir quand il est appelé pourrait être utile.

Néanmoins avec un migrate_schemas ciblé avec l'option -d ça ne devrait pas arriver puisque qu'on ne chargerait pas les ContentType d'un autre tenant avant, idem pour la création du schéma dans reate_tenant il me semble qu'on ne devrait pas avoir le temps de charger le cache des ContentType dans le schéma public, mais pareil il faudrait vérifier les appels à ContentType._add_to_cache (dans django/contrib/contentypes/models.py) et voir le tenant en cours à chaque appel (du django.db.connection.schema ou tenant).

#5

Mis à jour par Benjamin Dauvergne il y a environ 8 ans

Voilà j'ai préféré corriger le souci à la source, il faudrait que j'arrive à reproduire le problème pour voir si ça le corrige bien; peut-être en supprimant à la main des objets Permission au hasard dans un tenant.

#6

Mis à jour par Benjamin Dauvergne il y a environ 8 ans

Pas évident mais j'ai trouvé la source du problème. Il y a dans combo.data.apps ce code:

    def ready(self):
        from .library import library
        library.ready()

qui appelle donc ce code ci:

    def ready(self):
        # initialize the dictionary
        self.classes_by_content_str = {}
        for klass in self.classes:
            try:
                content_type = ContentType.objects.get_for_model(klass)
            except RuntimeError:
                # this is for RuntimeError: Error creating new content types.
                # Please make sure contenttypes is migrated before trying to
                # migrate apps individually.
                #
                # If that happens we switch to lazy initialization.
                self.classes_by_content_str = None
                break
            content_type_str_v = '%s_%s' % (content_type.app_label, content_type.model)
            self.classes_by_content_str[content_type_str_v] = klass

or au moment du ready() on est pas dans le contexte du tenant mais dans le contexte "public". L'utilisation du modèle ContentType force l'initialisation du cache de ContentType (ainsi que la création des modèles qui n'existeraient pas normalement). Ensuite lors du passage dans le premier tenant, le signal post_migrate est appelé pour l'application momo qui est la première dans la liste, cela appelle le handler de django.contrib.auth pour la création des permissions, qui recherche le ContentType pour momoiconcell qui n'a pas le même id dans la base public que dans le tenant de portail-agent.alfortville.fr et boum; il faut en plus que l'id trouvé dans la table du schéma public ne correspondent à aucun id existant, sinon l'erreur devient invisible (mais les permissions sont foirées). Ce qui est bizarre c'est que la table django_content_type ne devrait même pas exister, mais je pense que la base a été initialisée à une époque où on avait encore django.contrib.contenttypes listé dans SHARED_APPS.

Je pense qu'il faudrait remplacer ce code de combo par une utilisation de klass._meta.app_label, klass._meta.model_name car en plus il est contre-indiqué d'accéder aux modèles dans les méthodes ready()[1] et ici ContentType ne me semble pas servir à grand chose. Fred est-ce que tu te souviens pourquoi tu as utilisé ContentType plutôt que l'objet Meta des modèles ?

[1]: https://docs.djangoproject.com/en/1.9/ref/applications/#django.apps.AppConfig.ready

#7

Mis à jour par Benjamin Dauvergne il y a environ 8 ans

Le problème est d'ailleurs présent aussi sur portail-agents.vincennes.fr mais il n'est pas gênant:

 154 | Can add Meta for mobile            |              16 | add_momoiconcell
 155 | Can change Meta for mobile         |              16 | change_momoiconcell
 156 | Can delete Meta for mobile         |              16 | delete_momoiconcell
 160 | Can add Meta for mobile            |              38 | add_momoiconcell
 161 | Can change Meta for mobile         |              38 | change_momoiconcell
 162 | Can delete Meta for mobile         |              38 | delete_momoiconcell

idem sur fontenay:

 106 | Can add Meta for mobile            |              16 | add_momoiconcell
 107 | Can change Meta for mobile         |              16 | change_momoiconcell
 108 | Can delete Meta for mobile         |              16 | delete_momoiconcell

dès qu'on a le content_type_id == 16.

#9

Mis à jour par Frédéric Péters il y a environ 8 ans

go.

#10

Mis à jour par Frédéric Péters il y a environ 8 ans

  • Lié à Bug #10308: erreur à l'exécution de commandes de manage avec le dernier django-tenant-schemas ajouté
#11

Mis à jour par Benjamin Dauvergne il y a presque 8 ans

  • Statut changé de Nouveau à Fermé

Corrigé dans django-tenant-schemas (notre fork):

commit 09175cc6d74f8891fcc2699f7f593839b9da1396
Author: Frédéric Péters <fpeters@entrouvert.com>
Date:   Wed Mar 16 11:05:41 2016 +0100

    fix custom content type cache __get__ method (#10308)

commit e2d86034c2e8a85d070a37123419b1f92c0b5fcd
Author: Frédéric Péters <fpeters@entrouvert.com>
Date:   Wed Mar 16 11:03:17 2016 +0100

    import ContentType, ContentTypeManager from contenttypes module (#10308)

commit e0c423e1f285dd1430213f67009c534652bcf605
Author: Benjamin Dauvergne <bdauvergne@entrouvert.com>
Date:   Sat Feb 6 15:03:10 2016 +0100

    clear ContentType on each schema change (fixes #9891)

    Also replace the cache dictionnary by a thread local variable to make it safe to
    use multitenancy with threads.

Formats disponibles : Atom PDF