Repository: learningequality/kolibri

Issue:
Integrity Error for SyncQueue
## Observed behavior
An integrity error in the syncqueue table is preventing Kolibri from starting

## Errors and logs
```
Operations to perform:
Apply all migrations: admin, analytics, auth, bookmarks, content, contenttypes, device, discovery,
exams, kolibriauth, lessons, logger, morango, notifications, sessions
Running migrations:
No migrations to apply.
Operations to perform:
Apply all migrations: admin, analytics, auth, bookmarks, content, contenttypes, device, discovery,
exams, kolibriauth, lessons, logger, morango, notifications, sessions
Running migrations:
Applying device.0019_syncqueue_and_status...Error: Traceback (most recent call last):
File "c:\python36\lib\site-packages\kolibri\dist\django\db\backends\utils.py", line 64, in execute
return self.cursor.execute(sql, params)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\backends\sqlite3\base.py", line 328, in execute
return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: UNIQUE constraint failed: device_syncqueue.user_id,
device_syncqueue.instance_id

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "c:\python36\lib\site-packages\kolibri\utils\cli.py", line 193, in invoke
initialize(**get_initialize_params())
File "c:\python36\lib\site-packages\kolibri\utils\main.py", line 311, in initialize
update(version, kolibri.__version__)
File "c:\python36\lib\site-packages\kolibri\utils\main.py", line 365, in update
_migrate_databases()
File "c:\python36\lib\site-packages\kolibri\utils\main.py", line 113, in _migrate_databases
call_command("migrate", interactive=False, database=database)
File "c:\python36\lib\site-packages\kolibri\dist\django\core\management\__init__.py", line 131, in
call_command
return command.execute(*args, **defaults)
File "c:\python36\lib\site-packages\kolibri\dist\django\core\management\base.py", line 330, in execute
output = self.handle(*args, **options)
File "c:\python36\lib\site-packages\kolibri\dist\django\core\management\commands\migrate.py", line
204, in handle
fake_initial=fake_initial,
File "c:\python36\lib\site-packages\kolibri\dist\django\db\migrations\executor.py", line 115, in migrate
state = self._migrate_all_forwards(state, plan, full_plan, fake=fake, fake_initial=fake_initial)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\migrations\executor.py", line 145, in
_migrate_all_forwards
state = self.apply_migration(state, migration, fake=fake, fake_initial=fake_initial)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\migrations\executor.py", line 244, in
apply_migration
state = migration.apply(state, schema_editor)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\migrations\migration.py", line 129, in apply
operation.database_forwards(self.app_label, schema_editor, old_state, project_state)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\migrations\operations\models.py", line 536, in
database_forwards
getattr(new_model._meta, self.option_name, set()),
File "c:\python36\lib\site-packages\kolibri\dist\django\db\backends\base\schema.py", line 369, in
alter_unique_together
self.execute(self._create_unique_sql(model, columns))
File "c:\python36\lib\site-packages\kolibri\dist\django\db\backends\base\schema.py", line 136, in
execute
cursor.execute(sql, params)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\backends\utils.py", line 64, in execute

return self.cursor.execute(sql, params)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\utils.py", line 94, in __exit__
six.reraise(dj_exc_type, dj_exc_value, traceback)
File "c:\python36\lib\site-packages\kolibri\dist\django\utils\six.py", line 685, in reraise
raise value.with_traceback(tb)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\backends\utils.py", line 64, in execute
return self.cursor.execute(sql, params)
File "c:\python36\lib\site-packages\kolibri\dist\django\db\backends\sqlite3\base.py", line 328, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: UNIQUE constraint failed: device_syncqueue.user_id,
device_syncqueue.instance_id```
```

## Expected behavior
Kolibri should start regardless!

## User-facing consequences
Utter breakage of Kolibri.

## Steps to reproduce
Unclear.

## Context
Kolibri 0.16.1



Discussion:

Resolving patch:
diff --git a/kolibri/core/device/migrations/0019_syncqueue_and_status.py b/kolibri/core/device/migrations/0019_syncqueue_and_status.py
--- a/kolibri/core/device/migrations/0019_syncqueue_and_status.py
+++ b/kolibri/core/device/migrations/0019_syncqueue_and_status.py
@@ -4,7 +4,7 @@
 
 import django.db.models.deletion
 import morango.models.fields.uuids
-from django.conf import settings
+from django.db import connection
 from django.db import migrations
 from django.db import models
 
@@ -20,6 +20,14 @@ class Migration(migrations.Migration):
             model_name="usersyncstatus",
             name="queued",
         ),
+        migrations.RunSQL(
+            [
+                "DELETE FROM device_syncqueue;"
+                if "sqlite" in connection.vendor
+                else "TRUNCATE TABLE device_syncqueue;"
+            ],
+            hints={"is_syncqueue": True},
+        ),
         migrations.AddField(
             model_name="syncqueue",
             name="attempts",
diff --git a/kolibri/core/device/models.py b/kolibri/core/device/models.py
--- a/kolibri/core/device/models.py
+++ b/kolibri/core/device/models.py
@@ -483,9 +483,8 @@ def allow_relation(self, obj1, obj2, **hints):
 
     def allow_migrate(self, db, app_label, model_name=None, **hints):
         """Ensure that the SyncQueue models get created on the right database."""
-        if (
-            app_label == SyncQueue._meta.app_label
-            and model_name == SyncQueue._meta.model_name
+        if app_label == SyncQueue._meta.app_label and (
+            model_name == SyncQueue._meta.model_name or hints.get("is_syncqueue")
         ):
             # The SyncQueue model should be migrated only on the SYNC_QUEUE database.
             return db == SYNC_QUEUE


Test patch: