diff --git a/activities/migrations/0008_state_and_post_indexes.py b/activities/migrations/0008_state_and_post_indexes.py new file mode 100644 index 0000000..d09a79d --- /dev/null +++ b/activities/migrations/0008_state_and_post_indexes.py @@ -0,0 +1,84 @@ +# Generated by Django 4.1.4 on 2023-01-01 17:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("activities", "0007_post_stats"), + ] + + operations = [ + migrations.AddIndex( + model_name="fanout", + index=models.Index( + fields=["state", "state_attempted"], name="ix_fanout_state_attempted" + ), + ), + migrations.AddIndex( + model_name="fanout", + index=models.Index( + condition=models.Q(("state_locked_until__isnull", False)), + fields=["state_locked_until", "state"], + name="ix_fanout_state_locked", + ), + ), + migrations.AddIndex( + model_name="hashtag", + index=models.Index( + fields=["state", "state_attempted"], name="ix_hashtag_state_attempted" + ), + ), + migrations.AddIndex( + model_name="hashtag", + index=models.Index( + condition=models.Q(("state_locked_until__isnull", False)), + fields=["state_locked_until", "state"], + name="ix_hashtag_state_locked", + ), + ), + migrations.AddIndex( + model_name="post", + index=models.Index( + fields=["visibility", "local", "published"], + name="ix_post_local_public_published", + ), + ), + migrations.AddIndex( + model_name="post", + index=models.Index( + fields=["visibility", "local", "created"], + name="ix_post_local_public_created", + ), + ), + migrations.AddIndex( + model_name="post", + index=models.Index( + fields=["state", "state_attempted"], name="ix_post_state_attempted" + ), + ), + migrations.AddIndex( + model_name="post", + index=models.Index( + condition=models.Q(("state_locked_until__isnull", False)), + fields=["state_locked_until", "state"], + name="ix_post_state_locked", + ), + ), + migrations.AddIndex( + model_name="postattachment", + index=models.Index( + fields=["state", "state_attempted"], + name="ix_postattachm_state_attempted", + ), + ), + migrations.AddIndex( + model_name="postattachment", + index=models.Index( + condition=models.Q(("state_locked_until__isnull", False)), + fields=["state_locked_until", "state"], + name="ix_postattachm_state_locked", + ), + ), + ] diff --git a/activities/models/post.py b/activities/models/post.py index bbab9a0..6677f2f 100644 --- a/activities/models/post.py +++ b/activities/models/post.py @@ -297,6 +297,14 @@ class Post(StatorModel): class Meta: indexes = [ GinIndex(fields=["hashtags"], name="hashtags_gin"), + models.Index( + fields=["visibility", "local", "published"], + name="ix_post_local_public_published", + ), + models.Index( + fields=["visibility", "local", "created"], + name="ix_post_local_public_created", + ), ] class urls(urlman.Urls): diff --git a/stator/models.py b/stator/models.py index 8991a49..9adba6d 100644 --- a/stator/models.py +++ b/stator/models.py @@ -4,6 +4,7 @@ from typing import ClassVar, cast from asgiref.sync import sync_to_async from django.db import models, transaction +from django.db.models.signals import class_prepared from django.utils import timezone from django.utils.functional import classproperty @@ -37,6 +38,40 @@ class StateField(models.CharField): return value +def add_stator_indexes(sender, **kwargs): + """ + Inject Indexes used by StatorModel in to any subclasses. This sidesteps the + current Django inability to inherit indexes when the Model subclass defines + its own indexes. + """ + if issubclass(sender, StatorModel): + indexes = [ + models.Index( + fields=["state", "state_attempted"], + name=f"ix_{sender.__name__.lower()[:11]}_state_attempted", + ), + models.Index( + fields=["state_locked_until", "state"], + condition=models.Q(state_locked_until__isnull=False), + name=f"ix_{sender.__name__.lower()[:11]}_state_locked", + ), + ] + + if not sender._meta.indexes: + # Meta.indexes needs to not be None to trigger Django behaviors + sender.Meta.indexes = [] + + for idx in indexes: + sender._meta.indexes.append(idx) + + +# class_prepared might become deprecated [1]. If it's removed, the named Index +# injection would need to happen in a metaclass subclass of ModelBase's _prepare() +# +# [1] https://code.djangoproject.com/ticket/24313 +class_prepared.connect(add_stator_indexes) + + class StatorModel(models.Model): """ A model base class that has a state machine backing it, with tasks to work diff --git a/users/migrations/0008_follow_boosts.py b/users/migrations/0008_follow_boosts.py index 592c97e..40f8597 100644 --- a/users/migrations/0008_follow_boosts.py +++ b/users/migrations/0008_follow_boosts.py @@ -14,8 +14,7 @@ class Migration(migrations.Migration): model_name="follow", name="boosts", field=models.BooleanField( - default=True, - help_text="Also follow boosts from this user", + default=True, help_text="Also follow boosts from this user" ), ), ] diff --git a/users/migrations/0009_state_and_post_indexes.py b/users/migrations/0009_state_and_post_indexes.py new file mode 100644 index 0000000..b845c49 --- /dev/null +++ b/users/migrations/0009_state_and_post_indexes.py @@ -0,0 +1,66 @@ +# Generated by Django 4.1.4 on 2023-01-01 17:34 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("users", "0008_follow_boosts"), + ] + + operations = [ + migrations.AlterField( + model_name="identity", + name="restriction", + field=models.IntegerField( + choices=[(0, "None"), (1, "Limited"), (2, "Blocked")], + db_index=True, + default=0, + ), + ), + migrations.AddIndex( + model_name="inboxmessage", + index=models.Index( + fields=["state", "state_attempted"], + name="ix_inboxmessag_state_attempted", + ), + ), + migrations.AddIndex( + model_name="inboxmessage", + index=models.Index( + condition=models.Q(("state_locked_until__isnull", False)), + fields=["state_locked_until", "state"], + name="ix_inboxmessag_state_locked", + ), + ), + migrations.AddIndex( + model_name="passwordreset", + index=models.Index( + fields=["state", "state_attempted"], + name="ix_passwordres_state_attempted", + ), + ), + migrations.AddIndex( + model_name="passwordreset", + index=models.Index( + condition=models.Q(("state_locked_until__isnull", False)), + fields=["state_locked_until", "state"], + name="ix_passwordres_state_locked", + ), + ), + migrations.AddIndex( + model_name="report", + index=models.Index( + fields=["state", "state_attempted"], name="ix_report_state_attempted" + ), + ), + migrations.AddIndex( + model_name="report", + index=models.Index( + condition=models.Q(("state_locked_until__isnull", False)), + fields=["state_locked_until", "state"], + name="ix_report_state_locked", + ), + ), + ] diff --git a/users/models/identity.py b/users/models/identity.py index c263fde..6fcfd47 100644 --- a/users/models/identity.py +++ b/users/models/identity.py @@ -203,7 +203,7 @@ class Identity(StatorModel): # Admin-only moderation fields sensitive = models.BooleanField(default=False) restriction = models.IntegerField( - choices=Restriction.choices, default=Restriction.none + choices=Restriction.choices, default=Restriction.none, db_index=True ) admin_notes = models.TextField(null=True, blank=True)