mirror of https://github.com/zulip/zulip.git
migrations: Fix migration to set default for can_mention_group.
This commit updates `0455_set_default_for_can_mention_group` migration to be more efficient when running for a large number of UserGroup objects. Previously, we did a loop over all UserGroup objects and then did a `bulk_update`. All this happened in a single transaction and the transaction was being hold for unacceptably long time for a server with large number of user groups. Also the SQL generated by Django for `bulk_update` took almost quadratic time to evaluate, as the SQL had linear length "CASE" statement which was being resolved for each row. We instead now use ".update" so that we can write the migration without using loop and update the objects in batches of size 1000 so that we do not hold a transaction for very long time. This also helps in avoiding the inefficient SQL that was being executed due to using `bulk_update`. We also update the queries to exclude the groups that already have `can_mention_group` set to a non-null value, as this will help in migration completing quickly when running it more than once.
This commit is contained in:
parent
02c279c6b6
commit
1d3f5a0368
|
@ -1,36 +1,51 @@
|
|||
# Generated by Django 4.2.1 on 2023-06-12 10:47
|
||||
|
||||
from django.db import migrations
|
||||
from django.db import migrations, transaction
|
||||
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
|
||||
from django.db.migrations.state import StateApps
|
||||
from django.db.models import Max, Min, OuterRef
|
||||
|
||||
|
||||
def set_default_value_for_can_mention_group(
|
||||
apps: StateApps, schema_editor: BaseDatabaseSchemaEditor
|
||||
) -> None:
|
||||
Realm = apps.get_model("zerver", "Realm")
|
||||
UserGroup = apps.get_model("zerver", "UserGroup")
|
||||
|
||||
groups_to_update = []
|
||||
for realm in Realm.objects.all():
|
||||
# The default value of `can_mention_group` is everyone group for
|
||||
# all groups except role-based system groups. For role-based system
|
||||
# groups, we set the value of `can_mention_group` to nobody group.
|
||||
nobody_group = UserGroup.objects.get(name="@role:nobody", realm=realm, is_system_group=True)
|
||||
everyone_group = UserGroup.objects.get(
|
||||
name="@role:everyone", realm=realm, is_system_group=True
|
||||
)
|
||||
for group in UserGroup.objects.filter(realm=realm):
|
||||
if group.is_system_group:
|
||||
group.can_mention_group = nobody_group
|
||||
else:
|
||||
group.can_mention_group = everyone_group
|
||||
groups_to_update.append(group)
|
||||
BATCH_SIZE = 1000
|
||||
max_id = UserGroup.objects.filter(can_mention_group=None).aggregate(Max("id"))["id__max"]
|
||||
|
||||
UserGroup.objects.bulk_update(groups_to_update, ["can_mention_group"])
|
||||
if max_id is None:
|
||||
# Do nothing if there are no UserGroups on the server.
|
||||
return
|
||||
|
||||
lower_bound = UserGroup.objects.filter(can_mention_group=None).aggregate(Min("id"))["id__min"]
|
||||
while lower_bound <= max_id:
|
||||
upper_bound = lower_bound + BATCH_SIZE - 1
|
||||
print(f"Processing batch {lower_bound} to {upper_bound} for UserGroup")
|
||||
|
||||
with transaction.atomic():
|
||||
UserGroup.objects.filter(
|
||||
id__range=(lower_bound, upper_bound), can_mention_group=None, is_system_group=True
|
||||
).update(
|
||||
can_mention_group=UserGroup.objects.filter(
|
||||
name="@role:nobody", realm=OuterRef("realm"), is_system_group=True
|
||||
).values("pk")
|
||||
)
|
||||
|
||||
UserGroup.objects.filter(
|
||||
id__range=(lower_bound, upper_bound), can_mention_group=None, is_system_group=False
|
||||
).update(
|
||||
can_mention_group=UserGroup.objects.filter(
|
||||
name="@role:everyone", realm=OuterRef("realm"), is_system_group=True
|
||||
).values("pk")
|
||||
)
|
||||
|
||||
lower_bound += BATCH_SIZE
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
atomic = False
|
||||
|
||||
dependencies = [
|
||||
("zerver", "0454_usergroup_can_mention_group"),
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue