From 65c400e06d59db21a741ed07c595be0c5b649dc2 Mon Sep 17 00:00:00 2001 From: m-e-l-u-h-a-n Date: Sun, 18 Apr 2021 14:58:39 +0530 Subject: [PATCH] api: Add zulip_version and zulip_feature_level in restart event. This help mobile and terminal clients understand whether a server restart changed API feature levels or not, which in turn determines whether they will need to resynchronize their data. Also add tests and documentation for this previously undocumented event type. Fixes: #18205. --- frontend_tests/node_tests/lib/events.js | 2 + templates/zerver/api/changelog.md | 5 ++ zerver/lib/event_schema.py | 8 +++- zerver/openapi/zulip.yaml | 62 +++++++++++++++++++++++++ zerver/tests/test_event_system.py | 3 ++ zerver/tests/test_events.py | 12 ++++- zerver/tornado/event_queue.py | 8 +++- 7 files changed, 97 insertions(+), 3 deletions(-) diff --git a/frontend_tests/node_tests/lib/events.js b/frontend_tests/node_tests/lib/events.js index d029372d73..e09b5679df 100644 --- a/frontend_tests/node_tests/lib/events.js +++ b/frontend_tests/node_tests/lib/events.js @@ -517,6 +517,8 @@ exports.fixtures = { restart: { type: "restart", + zulip_version: "4.0-dev+git", + zulip_feature_level: 55, server_generation: 2, immediate: true, }, diff --git a/templates/zerver/api/changelog.md b/templates/zerver/api/changelog.md index 75fdff5b02..26d490e0a9 100644 --- a/templates/zerver/api/changelog.md +++ b/templates/zerver/api/changelog.md @@ -10,6 +10,11 @@ below features are supported. ## Changes in Zulip 4.0 +**Feature level 59** + +* [`GET /events`](/api/get-events): Added new `zulip_version` and + `zulip_feature_level` fields to the `restart` event. + **Feature level 58** * [`POST /register`](/api/register-queue): Added the new diff --git a/zerver/lib/event_schema.py b/zerver/lib/event_schema.py index d9eb8ea6e4..c83a2a91ee 100644 --- a/zerver/lib/event_schema.py +++ b/zerver/lib/event_schema.py @@ -1142,7 +1142,13 @@ def check_realm_user_update( restart_event = event_dict_type( - required_keys=[("type", Equals("restart")), ("server_generation", int), ("immediate", bool)] + required_keys=[ + ("type", Equals("restart")), + ("zulip_version", str), + ("zulip_feature_level", int), + ("server_generation", int), + ("immediate", bool), + ] ) check_restart_event = make_checker(restart_event) diff --git a/zerver/openapi/zulip.yaml b/zerver/openapi/zulip.yaml index 761df9dbca..9e92fbc966 100644 --- a/zerver/openapi/zulip.yaml +++ b/zerver/openapi/zulip.yaml @@ -3114,6 +3114,68 @@ paths: "realm_id": 2, "id": 0, } + - type: object + description: | + Event sent to all the users whenever the Zulip server restarts. + + Specifically, this event is sent whenever the Tornado process + for the user is restarted; in particular, this will always happen + when the Zulip server is upgraded. + + Clients can use this event to know when they should get a new + event queue after a server upgrade. Clients doing so must implement + a random delay strategy to spread such restarts over 10 minutes or + more to avoid creating a synchronized thundering herd effect. + properties: + id: + $ref: "#/components/schemas/EventIdSchema" + type: + allOf: + - $ref: "#/components/schemas/EventTypeSchema" + - enum: + - restart + zulip_version: + type: string + description: | + The Zulip version number, in the format where this appears + in the [server_settings](/api/get-server-settings) and + [register](/api/register-queue) responses. + + **Changes**: New in Zulip 4.0 (feature level 59). + zulip_feature_level: + type: integer + description: | + The [Zulip feature level](/api/changelog) of the server + after the restart. + + Clients can safely avoid refetching their state and + creating a new event queue when the API feature level has not + changed, or when they know the specific feature level change + is not relevant to the client (E.g. it just adds a new endpoint + that the client doesn't use). + + **Changes**: New in Zulip 4.0 (feature level 59). + immediate: + type: boolean + description: | + Whether the client should fetch a new event queue immediately, + rather than using a backoff strategy to avoid thundering herds. + A Zulip development server uses this parameter to reload + clients immediately. + server_generation: + type: integer + description: | + The timestamp at which the server started. + additionalProperties: false + example: + { + "id": 0, + "immediate": True, + "server_generation": 1619334181, + "type": "restart", + "zulip_feature_level": 57, + "zulip_version": "4.0-dev+git", + } - type: object additionalProperties: false description: | diff --git a/zerver/tests/test_event_system.py b/zerver/tests/test_event_system.py index 0578d30f0f..0f0e44f7ff 100644 --- a/zerver/tests/test_event_system.py +++ b/zerver/tests/test_event_system.py @@ -6,6 +6,7 @@ import orjson from django.conf import settings from django.http import HttpRequest, HttpResponse +from version import API_FEATURE_LEVEL, ZULIP_VERSION from zerver.lib.actions import check_send_message, do_change_user_role, do_set_realm_property from zerver.lib.event_schema import check_restart_event from zerver.lib.events import fetch_initial_state_data, get_raw_user_data @@ -877,6 +878,8 @@ class RestartEventsTest(ZulipTestCase): restart_event, dict( type="restart", + zulip_version=ZULIP_VERSION, + zulip_feature_level=API_FEATURE_LEVEL, server_generation=settings.SERVER_GENERATION, immediate=True, id=0, diff --git a/zerver/tests/test_events.py b/zerver/tests/test_events.py index c89a4cd7e0..e33daf5e67 100644 --- a/zerver/tests/test_events.py +++ b/zerver/tests/test_events.py @@ -161,7 +161,12 @@ from zerver.lib.event_schema import ( check_user_group_update, check_user_status, ) -from zerver.lib.events import apply_events, fetch_initial_state_data, post_process_state +from zerver.lib.events import ( + RestartEventException, + apply_events, + fetch_initial_state_data, + post_process_state, +) from zerver.lib.markdown import MentionData from zerver.lib.message import render_markdown from zerver.lib.test_classes import ZulipTestCase @@ -198,6 +203,7 @@ from zerver.openapi.openapi import validate_against_openapi_schema from zerver.tornado.event_queue import ( allocate_client_descriptor, clear_client_event_queues_for_testing, + send_restart_events, ) from zerver.views.realm_playgrounds import access_playground_by_id @@ -1938,6 +1944,10 @@ class NormalActionsTest(BaseAction): events = self.verify_action(lambda: do_set_zoom_token(self.user_profile, None)) check_has_zoom_token("events[0]", events[0], value=False) + def test_restart_event(self) -> None: + with self.assertRaises(RestartEventException): + self.verify_action(lambda: send_restart_events(immediate=True)) + class RealmPropertyActionTest(BaseAction): def do_set_realm_property_test(self, name: str) -> None: diff --git a/zerver/tornado/event_queue.py b/zerver/tornado/event_queue.py index 555e20e0b6..692ea9b93c 100644 --- a/zerver/tornado/event_queue.py +++ b/zerver/tornado/event_queue.py @@ -33,6 +33,7 @@ from django.conf import settings from django.utils.translation import gettext as _ from typing_extensions import TypedDict +from version import API_FEATURE_LEVEL, ZULIP_VERSION from zerver.decorator import cachify from zerver.lib.message import MessageDict from zerver.lib.narrow import build_narrow_filter @@ -572,7 +573,12 @@ def load_event_queues(port: int) -> None: def send_restart_events(immediate: bool = False) -> None: - event: Dict[str, Any] = dict(type="restart", server_generation=settings.SERVER_GENERATION) + event: Dict[str, Any] = dict( + type="restart", + zulip_version=ZULIP_VERSION, + zulip_feature_level=API_FEATURE_LEVEL, + server_generation=settings.SERVER_GENERATION, + ) if immediate: event["immediate"] = True for client in clients.values():