mirror of https://github.com/zulip/zulip.git
message_fetch: Take a REPEATABLE READ READ ONLY when fetching messages.
This commit is contained in:
parent
0f521fba41
commit
9a682fb20a
|
@ -1,6 +1,8 @@
|
||||||
from typing import Dict, Iterable, List, Optional, Tuple, Union
|
from typing import Dict, Iterable, List, Optional, Tuple, Union
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
from django.contrib.auth.models import AnonymousUser
|
from django.contrib.auth.models import AnonymousUser
|
||||||
|
from django.db import connection, transaction
|
||||||
from django.http import HttpRequest, HttpResponse
|
from django.http import HttpRequest, HttpResponse
|
||||||
from django.utils.html import escape as escape_html
|
from django.utils.html import escape as escape_html
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
@ -169,6 +171,31 @@ def get_messages_backend(
|
||||||
assert log_data is not None
|
assert log_data is not None
|
||||||
log_data["extra"] = "[{}]".format(",".join(verbose_operators))
|
log_data["extra"] = "[{}]".format(",".join(verbose_operators))
|
||||||
|
|
||||||
|
with transaction.atomic(durable=True):
|
||||||
|
# We're about to perform a search, and then get results from
|
||||||
|
# it; this is done across multiple queries. To prevent race
|
||||||
|
# conditions, we want the messages returned to be consistent
|
||||||
|
# with the version of the messages that was searched, to
|
||||||
|
# prevent changes which happened between them from leaking to
|
||||||
|
# clients who should not be able to see the new values, and
|
||||||
|
# when messages are deleted in between. We set up
|
||||||
|
# repeatable-read isolation for this transaction, so that we
|
||||||
|
# prevent both phantom reads and non-repeatable reads.
|
||||||
|
#
|
||||||
|
# In a read-only repeatable-read transaction, it is not
|
||||||
|
# possible to encounter deadlocks or need retries due to
|
||||||
|
# serialization errors.
|
||||||
|
#
|
||||||
|
# You can only set the isolation level before any queries in
|
||||||
|
# the transaction, meaning it must be the top-most
|
||||||
|
# transaction, which durable=True establishes. Except in
|
||||||
|
# tests, where durable=True is a lie, because there is an
|
||||||
|
# outer transaction for each test. We thus skip this command
|
||||||
|
# in tests, since it would fail.
|
||||||
|
if not settings.TEST_SUITE: # nocoverage
|
||||||
|
cursor = connection.cursor()
|
||||||
|
cursor.execute("SET TRANSACTION ISOLATION LEVEL REPEATABLE READ READ ONLY")
|
||||||
|
|
||||||
query_info = fetch_messages(
|
query_info = fetch_messages(
|
||||||
narrow=narrow,
|
narrow=narrow,
|
||||||
user_profile=user_profile,
|
user_profile=user_profile,
|
||||||
|
@ -205,7 +232,9 @@ def get_messages_backend(
|
||||||
message_ids = [row[0] for row in rows]
|
message_ids = [row[0] for row in rows]
|
||||||
|
|
||||||
# TODO: This could be done with an outer join instead of two queries
|
# TODO: This could be done with an outer join instead of two queries
|
||||||
um_rows = UserMessage.objects.filter(user_profile=user_profile, message_id__in=message_ids)
|
um_rows = UserMessage.objects.filter(
|
||||||
|
user_profile=user_profile, message_id__in=message_ids
|
||||||
|
)
|
||||||
user_message_flags = {um.message_id: um.flags_list() for um in um_rows}
|
user_message_flags = {um.message_id: um.flags_list() for um in um_rows}
|
||||||
|
|
||||||
for message_id in message_ids:
|
for message_id in message_ids:
|
||||||
|
|
Loading…
Reference in New Issue