populate_db: Temporarily remove post-delete signal on AlertWord.

The post-delete signal on AlertWord clears the realm cache; when it is
called repeatedly, this results in re-fetching the realm object O(n)
times, where n scales by number of users in the database.

Disconnect this cache-clearing signal before removing the AlertWord
entries, and reconnect it afterwards.  This is not thread-safe, but
this section is single-threaded.  It is also probably unnecessary to
re-connect the signal, as rest of `./manage.py populate_db` does not
delete AlertWord objects, but cleanliness dictates doing the
re-connection.

This drops the time to repeatedly run:
    python3 ./manage.py populate_db --num-messages=0 --extra-users=1000

...from 47 seconds to 36 seconds.
This commit is contained in:
Alex Vandiver 2023-02-17 17:29:21 +00:00 committed by Josh Klar
parent 7cafbefdef
commit 7feda75c5f
1 changed files with 9 additions and 0 deletions

View File

@ -14,6 +14,7 @@ from django.core.management import call_command
from django.core.management.base import BaseCommand, CommandParser from django.core.management.base import BaseCommand, CommandParser
from django.db import connection from django.db import connection
from django.db.models import F from django.db.models import F
from django.db.models.signals import post_delete
from django.utils.timezone import now as timezone_now from django.utils.timezone import now as timezone_now
from django.utils.timezone import timedelta as timezone_timedelta from django.utils.timezone import timedelta as timezone_timedelta
@ -59,6 +60,7 @@ from zerver.models import (
UserMessage, UserMessage,
UserPresence, UserPresence,
UserProfile, UserProfile,
flush_alert_word,
get_client, get_client,
get_or_create_huddle, get_or_create_huddle,
get_realm, get_realm,
@ -103,9 +105,15 @@ def clear_database() -> None:
).flush_all() ).flush_all()
model: Any = None # Hack because mypy doesn't know these are model classes model: Any = None # Hack because mypy doesn't know these are model classes
# The after-delete signal on this just updates caches, and slows
# down the deletion noticeably. Remove the signal and replace it
# after we're done.
post_delete.disconnect(flush_alert_word, sender=AlertWord)
for model in [ for model in [
Message, Message,
Stream, Stream,
AlertWord,
UserProfile, UserProfile,
Recipient, Recipient,
Realm, Realm,
@ -117,6 +125,7 @@ def clear_database() -> None:
]: ]:
model.objects.all().delete() model.objects.all().delete()
Session.objects.all().delete() Session.objects.all().delete()
post_delete.connect(flush_alert_word, sender=AlertWord)
def subscribe_users_to_streams(realm: Realm, stream_dict: Dict[str, Dict[str, Any]]) -> None: def subscribe_users_to_streams(realm: Realm, stream_dict: Dict[str, Dict[str, Any]]) -> None: