Opened 4 years ago

Closed 4 years ago

#24627 closed Bug (needsinfo)

Race condition like error in migration

Reported by: Martin Häcker Owned by: nobody
Component: Migrations Version: 1.8
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Hi there,

I'm having a strange error in some migrations. Let me describe:

At first I had several migrations, one renamed a model process.ProcessDef to process.ProcessDefinition, then a later one would rename a field on that model.

        migrations.RenameModel(
            old_name='ProcInstance',
            new_name='ProcessInstance',
        ),
        migrations.RenameField(
            model_name='processinstance',
            old_name='status',
            new_name='runstatus',
        ),

This creates a stack trace that looks like this:

  File "./manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/core/management/__init__.py", line 338, in execute_from_command_line
    utility.execute()
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/core/management/__init__.py", line 330, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/core/management/base.py", line 390, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/core/management/base.py", line 441, in execute
    output = self.handle(*args, **options)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/core/management/commands/migrate.py", line 221, in handle
    executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/migrations/executor.py", line 110, in migrate
    self.apply_migration(states[migration], migration, fake=fake, fake_initial=fake_initial)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/migrations/executor.py", line 147, in apply_migration
    state = migration.apply(state, schema_editor)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/migrations/migration.py", line 115, in apply
    operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/migrations/operations/fields.py", line 275, in database_forwards
    to_model._meta.get_field(self.new_name),
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 484, in alter_field
    old_db_params, new_db_params, strict)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/backends/sqlite3/schema.py", line 200, in _alter_field
    self._remake_table(model, alter_fields=[(old_field, new_field)])
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/backends/sqlite3/schema.py", line 137, in _remake_table
    self.create_model(temp_model)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 232, in create_model
    definition, extra_params = self.column_sql(model, field)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/backends/base/schema.py", line 131, in column_sql
    db_params = field.db_parameters(connection=self.connection)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/models/fields/related.py", line 1989, in db_parameters
    return {"type": self.db_type(connection), "check": []}
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/models/fields/related.py", line 1980, in db_type
    rel_field = self.related_field
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/models/fields/related.py", line 1886, in related_field
    return self.foreign_related_fields[0]
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/models/fields/related.py", line 1620, in foreign_related_fields
    return tuple(rhs_field for lhs_field, rhs_field in self.related_fields)
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/models/fields/related.py", line 1607, in related_fields
    self._related_fields = self.resolve_related_fields()
  File "/Users/dwt/.virtualenvs/pycess/lib/python3.4/site-packages/django/db/models/fields/related.py", line 1592, in resolve_related_fields
    raise ValueError('Related model %r cannot be resolved' % self.rel.to)
ValueError: Related model 'process.ProcessDef' cannot be resolved

That is, it seems as if the internal migration logic is missing the fact that it has just renamed a model, and is trying to look up a relation by it's old un-renamed name - and then failing.

I have tried to reduce this to one squashed migration, but I'm not sure I entirely succeeded, as the bug seems to be transient with this migration, i.e. I get it in about 80-90% of cases, and removing more from it which *should* be unrelated seems to bring this ratio down quite drastically.

So to sum it up, it feels like a race condition.

Anyway, here it is:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations
from django.conf import settings


class Migration(migrations.Migration):

    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='FieldDef',
            fields=[
                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
                ('name', models.CharField(max_length=200)),
                ('descript', models.CharField(max_length=200)),
                ('fieldhelp', models.CharField(max_length=200)),
                ('fieldtype', models.PositiveSmallIntegerField()),
                ('length', models.PositiveSmallIntegerField()),
                ('editable', models.NullBooleanField()),
                ('must', models.NullBooleanField()),
                ('type', models.PositiveSmallIntegerField()),
                ('parent', models.ForeignKey(to='process.FieldDef')),
            ],
        ),
        migrations.CreateModel(
            name='ProcessDef',
            fields=[
                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
                ('name', models.CharField(max_length=200)),
                ('descript', models.CharField(max_length=200)),
                ('status', models.PositiveSmallIntegerField()),
                ('version', models.PositiveSmallIntegerField()),
                ('refering', models.ForeignKey(to='process.ProcessDef')),
            ],
        ),
        migrations.CreateModel(
            name='ProcInstance',
            fields=[
                ('id', models.AutoField(primary_key=True, verbose_name='ID', serialize=False, auto_created=True)),
                ('starttime', models.DateTimeField()),
                ('stoptime', models.DateTimeField()),
                ('status', models.PositiveSmallIntegerField()),
                ('process', models.ForeignKey(to='process.ProcessDef')),
            ],
        ),
        migrations.AlterField(
            model_name='processdef',
            name='refering',
            field=models.ForeignKey(to='process.ProcessDef', null=True),
        ),
        migrations.AlterField(
            model_name='processdef',
            name='refering',
            field=models.ForeignKey(to='process.ProcessDef', blank=True, null=True),
        ),
        migrations.RenameModel(
            old_name='ProcessDef',
            new_name='ProcessDefinition',
        ),
        migrations.RenameModel(
            old_name='ProcInstance',
            new_name='ProcessInstance',
        ),
        migrations.RenameField(
            model_name='processinstance',
            old_name='status',
            new_name='runstatus',
        ),
    ]

You can see the project where this is from at https://github.com/pycess/pycess for reference. There are some old bugs in the bug database that look kind of similar (e.g. #22319) but not quite, which is why I am filing this new ticket.

Change History (1)

comment:1 Changed 4 years ago by Tim Graham

Resolution: needsinfo
Status: newclosed

I couldn't reproduce the error using the provided migration. I ran python manage.py migrate and python manage.py migrate process zero several times in succession without an error. If you could provide a project we could download to reproduce the error, please reopen. Thanks!

Note: See TracTickets for help on using tickets.
Back to Top