mirror of https://github.com/zulip/zulip.git
django_api: Rename 'send_event' to 'send_event_rollback_unsafe'.
This commit renames the 'send_event' function to 'send_event_rollback_unsafe' to reflect the fact that it doesn't wait for the db transaction (within which it gets called, if any) to commit and sends event irrespective of commit or rollback. In most of the cases we don't want to send event in the case of rollbacks, so the caller should be aware that calling the function directly is rollback unsafe.
This commit is contained in:
parent
8e71806958
commit
e763d065a3
|
@ -20,7 +20,7 @@ from zerver.lib.stream_subscription import get_subscribed_stream_recipient_ids_f
|
||||||
from zerver.lib.topic import filter_by_topic_name_via_message
|
from zerver.lib.topic import filter_by_topic_name_via_message
|
||||||
from zerver.lib.user_message import DEFAULT_HISTORICAL_FLAGS, create_historical_user_messages
|
from zerver.lib.user_message import DEFAULT_HISTORICAL_FLAGS, create_historical_user_messages
|
||||||
from zerver.models import Message, Recipient, UserMessage, UserProfile
|
from zerver.models import Message, Recipient, UserMessage, UserProfile
|
||||||
from zerver.tornado.django_api import send_event, send_event_on_commit
|
from zerver.tornado.django_api import send_event_on_commit, send_event_rollback_unsafe
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
|
@ -96,7 +96,7 @@ def do_mark_all_as_read(user_profile: UserProfile, *, timeout: float | None = No
|
||||||
all=True,
|
all=True,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
send_event(user_profile.realm, event, [user_profile.id])
|
send_event_rollback_unsafe(user_profile.realm, event, [user_profile.id])
|
||||||
|
|
||||||
return count
|
return count
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ from zerver.lib.users import get_user_ids_who_can_access_user
|
||||||
from zerver.models import Client, UserPresence, UserProfile
|
from zerver.models import Client, UserPresence, UserProfile
|
||||||
from zerver.models.clients import get_client
|
from zerver.models.clients import get_client
|
||||||
from zerver.models.users import active_user_ids
|
from zerver.models.users import active_user_ids
|
||||||
from zerver.tornado.django_api import send_event
|
from zerver.tornado.django_api import send_event_rollback_unsafe
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ def send_presence_changed(
|
||||||
server_timestamp=time.time(),
|
server_timestamp=time.time(),
|
||||||
presence={presence_dict["client"]: presence_dict},
|
presence={presence_dict["client"]: presence_dict},
|
||||||
)
|
)
|
||||||
send_event(user_profile.realm, event, user_ids)
|
send_event_rollback_unsafe(user_profile.realm, event, user_ids)
|
||||||
|
|
||||||
|
|
||||||
def consolidate_client(client: Client) -> Client:
|
def consolidate_client(client: Client) -> Client:
|
||||||
|
|
|
@ -5,7 +5,7 @@ from zerver.lib.exceptions import JsonableError
|
||||||
from zerver.lib.stream_subscription import get_active_subscriptions_for_stream_id
|
from zerver.lib.stream_subscription import get_active_subscriptions_for_stream_id
|
||||||
from zerver.models import Realm, Stream, UserProfile
|
from zerver.models import Realm, Stream, UserProfile
|
||||||
from zerver.models.users import get_user_by_id_in_realm_including_cross_realm
|
from zerver.models.users import get_user_by_id_in_realm_including_cross_realm
|
||||||
from zerver.tornado.django_api import send_event
|
from zerver.tornado.django_api import send_event_rollback_unsafe
|
||||||
|
|
||||||
|
|
||||||
def do_send_typing_notification(
|
def do_send_typing_notification(
|
||||||
|
@ -32,7 +32,7 @@ def do_send_typing_notification(
|
||||||
if user.is_active and user.receives_typing_notifications
|
if user.is_active and user.receives_typing_notifications
|
||||||
]
|
]
|
||||||
|
|
||||||
send_event(realm, event, user_ids_to_notify)
|
send_event_rollback_unsafe(realm, event, user_ids_to_notify)
|
||||||
|
|
||||||
|
|
||||||
# check_send_typing_notification:
|
# check_send_typing_notification:
|
||||||
|
@ -98,4 +98,4 @@ def do_send_stream_typing_notification(
|
||||||
.values_list("user_profile_id", flat=True)
|
.values_list("user_profile_id", flat=True)
|
||||||
)
|
)
|
||||||
|
|
||||||
send_event(sender.realm, event, user_ids_to_notify)
|
send_event_rollback_unsafe(sender.realm, event, user_ids_to_notify)
|
||||||
|
|
|
@ -123,8 +123,8 @@ def do_delete_user_preserving_messages(user_profile: UserProfile) -> None:
|
||||||
conversations that they may have participated in.
|
conversations that they may have participated in.
|
||||||
|
|
||||||
Not recommended for general use due to the following quirks:
|
Not recommended for general use due to the following quirks:
|
||||||
* Does not live-update other clients via `send_event` about the
|
* Does not live-update other clients via `send_event_on_commit`
|
||||||
user's new name, email, or other attributes.
|
about the user's new name, email, or other attributes.
|
||||||
* Not guaranteed to clear caches containing the deleted users. The
|
* Not guaranteed to clear caches containing the deleted users. The
|
||||||
temporary user may be visible briefly in caches due to the
|
temporary user may be visible briefly in caches due to the
|
||||||
UserProfile model's post_save hook.
|
UserProfile model's post_save hook.
|
||||||
|
|
|
@ -24,7 +24,7 @@ def copy_default_settings(
|
||||||
settings_source: UserProfile | RealmUserDefault, target_profile: UserProfile
|
settings_source: UserProfile | RealmUserDefault, target_profile: UserProfile
|
||||||
) -> None:
|
) -> None:
|
||||||
# Important note: Code run from here to configure the user's
|
# Important note: Code run from here to configure the user's
|
||||||
# settings should not call send_event, as that would cause clients
|
# settings should not send events, as that would cause clients
|
||||||
# to throw an exception (we haven't sent the realm_user/add event
|
# to throw an exception (we haven't sent the realm_user/add event
|
||||||
# yet, so that event will include the updated details of target_profile).
|
# yet, so that event will include the updated details of target_profile).
|
||||||
#
|
#
|
||||||
|
|
|
@ -2052,11 +2052,11 @@ class ZulipTestCase(ZulipTestCaseMixin, TestCase):
|
||||||
# So explicitly change parameter name to 'notice' to work around this problem
|
# So explicitly change parameter name to 'notice' to work around this problem
|
||||||
with (
|
with (
|
||||||
mock.patch("zerver.tornado.event_queue.process_notification", lst.append),
|
mock.patch("zerver.tornado.event_queue.process_notification", lst.append),
|
||||||
# Some `send_event` calls need to be executed only after the current transaction
|
# Some `send_event_rollback_unsafe` calls need to be executed only after the
|
||||||
# commits (using `on_commit` hooks). Because the transaction in Django tests never
|
# current transaction commits (using `on_commit` hooks). Because the transaction
|
||||||
# commits (rather, gets rolled back after the test completes), such events would
|
# in Django tests never commits (rather, gets rolled back after the test completes),
|
||||||
# never be sent in tests, and we would be unable to verify them. Hence, we use
|
# such events would never be sent in tests, and we would be unable to verify them.
|
||||||
# this helper to make sure the `send_event` calls actually run.
|
# Hence, we use this helper to make sure the `send_event_rollback_unsafe` calls actually run.
|
||||||
self.captureOnCommitCallbacks(execute=True),
|
self.captureOnCommitCallbacks(execute=True),
|
||||||
):
|
):
|
||||||
yield lst
|
yield lst
|
||||||
|
|
|
@ -856,7 +856,7 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
||||||
self, include_realm_owners: bool = True
|
self, include_realm_owners: bool = True
|
||||||
) -> QuerySet["UserProfile"]:
|
) -> QuerySet["UserProfile"]:
|
||||||
"""Use this in contexts where we want administrative users as well as
|
"""Use this in contexts where we want administrative users as well as
|
||||||
bots with administrator privileges, like send_event calls for
|
bots with administrator privileges, like send_event_on_commit calls for
|
||||||
notifications to all administrator users.
|
notifications to all administrator users.
|
||||||
"""
|
"""
|
||||||
if include_realm_owners:
|
if include_realm_owners:
|
||||||
|
|
|
@ -252,7 +252,7 @@ from zerver.models.realm_audit_logs import AuditLogEventType
|
||||||
from zerver.models.streams import get_stream
|
from zerver.models.streams import get_stream
|
||||||
from zerver.models.users import get_user_by_delivery_email
|
from zerver.models.users import get_user_by_delivery_email
|
||||||
from zerver.openapi.openapi import validate_against_openapi_schema
|
from zerver.openapi.openapi import validate_against_openapi_schema
|
||||||
from zerver.tornado.django_api import send_event
|
from zerver.tornado.django_api import send_event_rollback_unsafe
|
||||||
from zerver.tornado.event_queue import (
|
from zerver.tornado.event_queue import (
|
||||||
allocate_client_descriptor,
|
allocate_client_descriptor,
|
||||||
clear_client_event_queues_for_testing,
|
clear_client_event_queues_for_testing,
|
||||||
|
@ -267,7 +267,7 @@ from zerver.worker.thumbnail import ensure_thumbnails
|
||||||
|
|
||||||
class BaseAction(ZulipTestCase):
|
class BaseAction(ZulipTestCase):
|
||||||
"""Core class for verifying the apply_event race handling logic as
|
"""Core class for verifying the apply_event race handling logic as
|
||||||
well as the event formatting logic of any function using send_event.
|
well as the event formatting logic of any function using send_event_rollback_unsafe.
|
||||||
|
|
||||||
See https://zulip.readthedocs.io/en/latest/subsystems/events-system.html#testing
|
See https://zulip.readthedocs.io/en/latest/subsystems/events-system.html#testing
|
||||||
for extensive design details for this testing system.
|
for extensive design details for this testing system.
|
||||||
|
@ -355,8 +355,8 @@ class BaseAction(ZulipTestCase):
|
||||||
|
|
||||||
events: list[dict[str, Any]] = []
|
events: list[dict[str, Any]] = []
|
||||||
|
|
||||||
# We want even those `send_event` calls which have been hooked to
|
# We want even those `send_event_rollback_unsafe` calls which have
|
||||||
# `transaction.on_commit` to execute in tests.
|
# been hooked to `transaction.on_commit` to execute in tests.
|
||||||
# See the comment in `ZulipTestCase.capture_send_event_calls`.
|
# See the comment in `ZulipTestCase.capture_send_event_calls`.
|
||||||
with self.captureOnCommitCallbacks(execute=True):
|
with self.captureOnCommitCallbacks(execute=True):
|
||||||
yield events
|
yield events
|
||||||
|
@ -1174,7 +1174,7 @@ class NormalActionsTest(BaseAction):
|
||||||
|
|
||||||
def test_heartbeat_event(self) -> None:
|
def test_heartbeat_event(self) -> None:
|
||||||
with self.verify_action(state_change_expected=False) as events:
|
with self.verify_action(state_change_expected=False) as events:
|
||||||
send_event(
|
send_event_rollback_unsafe(
|
||||||
self.user_profile.realm,
|
self.user_profile.realm,
|
||||||
create_heartbeat_event(),
|
create_heartbeat_event(),
|
||||||
[self.user_profile.id],
|
[self.user_profile.id],
|
||||||
|
|
|
@ -488,9 +488,9 @@ class DeleteMessageTest(ZulipTestCase):
|
||||||
|
|
||||||
def test_delete_event_sent_after_transaction_commits(self) -> None:
|
def test_delete_event_sent_after_transaction_commits(self) -> None:
|
||||||
"""
|
"""
|
||||||
Tests that `send_event` is hooked to `transaction.on_commit`. This is important, because
|
Tests that `send_event_rollback_unsafe` is hooked to `transaction.on_commit`.
|
||||||
we don't want to end up holding locks on message rows for too long if the event queue runs
|
This is important, because we don't want to end up holding locks on message rows
|
||||||
into a problem.
|
for too long if the event queue runs into a problem.
|
||||||
"""
|
"""
|
||||||
hamlet = self.example_user("hamlet")
|
hamlet = self.example_user("hamlet")
|
||||||
self.send_stream_message(hamlet, "Denmark")
|
self.send_stream_message(hamlet, "Denmark")
|
||||||
|
|
|
@ -1124,9 +1124,9 @@ class ReactionAPIEventTest(EmojiReactionBase):
|
||||||
|
|
||||||
def test_events_sent_after_transaction_commits(self) -> None:
|
def test_events_sent_after_transaction_commits(self) -> None:
|
||||||
"""
|
"""
|
||||||
Tests that `send_event` is hooked to `transaction.on_commit`. This is important, because
|
Tests that `send_event_rollback_unsafe` is hooked to `transaction.on_commit`.
|
||||||
we don't want to end up holding locks on message rows for too long if the event queue runs
|
This is important, because we don't want to end up holding locks on message rows
|
||||||
into a problem.
|
for too long if the event queue runs into a problem.
|
||||||
"""
|
"""
|
||||||
hamlet = self.example_user("hamlet")
|
hamlet = self.example_user("hamlet")
|
||||||
self.send_stream_message(hamlet, "Denmark")
|
self.send_stream_message(hamlet, "Denmark")
|
||||||
|
|
|
@ -187,9 +187,9 @@ class TestBasics(ZulipTestCase):
|
||||||
|
|
||||||
def test_submessage_event_sent_after_transaction_commits(self) -> None:
|
def test_submessage_event_sent_after_transaction_commits(self) -> None:
|
||||||
"""
|
"""
|
||||||
Tests that `send_event` is hooked to `transaction.on_commit`. This is important, because
|
Tests that `send_event_rollback_unsafe` is hooked to `transaction.on_commit`.
|
||||||
we don't want to end up holding locks on message rows for too long if the event queue runs
|
This is important, because we don't want to end up holding locks on message rows
|
||||||
into a problem.
|
for too long if the event queue runs into a problem.
|
||||||
"""
|
"""
|
||||||
hamlet = self.example_user("hamlet")
|
hamlet = self.example_user("hamlet")
|
||||||
message_id = self.send_stream_message(hamlet, "Denmark")
|
message_id = self.send_stream_message(hamlet, "Denmark")
|
||||||
|
|
|
@ -170,15 +170,21 @@ def send_notification_http(port: int, data: Mapping[str, Any]) -> None:
|
||||||
|
|
||||||
# The core function for sending an event from Django to Tornado (which
|
# The core function for sending an event from Django to Tornado (which
|
||||||
# will then push it to web and mobile clients for the target users).
|
# will then push it to web and mobile clients for the target users).
|
||||||
# By convention, send_event should only be called from
|
#
|
||||||
# zerver/actions/*.py, which helps make it easy to find event
|
# One should generally use `send_event_on_commit` unless there's a strong
|
||||||
# generation code.
|
# reason to use `send_event_rollback_unsafe` directly, as it doesn't wait
|
||||||
|
# for the db transaction (within which it gets called, if any) to commit
|
||||||
|
# and sends event irrespective of commit or rollback.
|
||||||
|
#
|
||||||
|
# By convention, `send_event_rollback_unsafe` / `send_event_on_commit`
|
||||||
|
# should only be called from zerver/actions/*.py, which helps make it
|
||||||
|
# easy to find event generation code.
|
||||||
#
|
#
|
||||||
# Every call point should be covered by a test in `test_events.py`,
|
# Every call point should be covered by a test in `test_events.py`,
|
||||||
# with the schema verified in `zerver/lib/event_schema.py`.
|
# with the schema verified in `zerver/lib/event_schema.py`.
|
||||||
#
|
#
|
||||||
# See https://zulip.readthedocs.io/en/latest/subsystems/events-system.html
|
# See https://zulip.readthedocs.io/en/latest/subsystems/events-system.html
|
||||||
def send_event(
|
def send_event_rollback_unsafe(
|
||||||
realm: Realm, event: Mapping[str, Any], users: Iterable[int] | Iterable[Mapping[str, Any]]
|
realm: Realm, event: Mapping[str, Any], users: Iterable[int] | Iterable[Mapping[str, Any]]
|
||||||
) -> None:
|
) -> None:
|
||||||
"""`users` is a list of user IDs, or in some special cases like message
|
"""`users` is a list of user IDs, or in some special cases like message
|
||||||
|
@ -203,4 +209,4 @@ def send_event(
|
||||||
def send_event_on_commit(
|
def send_event_on_commit(
|
||||||
realm: Realm, event: Mapping[str, Any], users: Iterable[int] | Iterable[Mapping[str, Any]]
|
realm: Realm, event: Mapping[str, Any], users: Iterable[int] | Iterable[Mapping[str, Any]]
|
||||||
) -> None:
|
) -> None:
|
||||||
transaction.on_commit(lambda: send_event(realm, event, users))
|
transaction.on_commit(lambda: send_event_rollback_unsafe(realm, event, users))
|
||||||
|
|
Loading…
Reference in New Issue